From 50a42e1d87959e25ee2cc97a01ae2a6e3ee6bad3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 9 Mar 2021 05:45:24 +0100 Subject: [PATCH 001/114] Rework the authenticator system, split the LDAP stuff into auth part and utils part --- data/actionsmap/yunohost.yml | 19 +- src/yunohost/authenticators/ldap_admin.py | 75 ++++++ src/yunohost/utils/ldap.py | 291 +++++++++++++++++++--- 3 files changed, 336 insertions(+), 49 deletions(-) create mode 100644 src/yunohost/authenticators/ldap_admin.py diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 290952aa3..741d75a22 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -33,18 +33,9 @@ # Global parameters # ############################# _global: - configuration: - authenticate: - - api - authenticator: - default: - vendor: ldap - help: admin_password - parameters: - uri: ldap://localhost:389 - base_dn: dc=yunohost,dc=org - user_rdn: cn=admin,dc=yunohost,dc=org - argument_auth: false + authentication: + api: ldap_admin + cli: null arguments: -v: full: --version @@ -1404,9 +1395,9 @@ tools: postinstall: action_help: YunoHost post-install api: POST /postinstall - configuration: + authentication: # We need to be able to run the postinstall without being authenticated, otherwise we can't run the postinstall - authenticate: false + api: null arguments: -d: full: --domain diff --git a/src/yunohost/authenticators/ldap_admin.py b/src/yunohost/authenticators/ldap_admin.py new file mode 100644 index 000000000..d7a1dadda --- /dev/null +++ b/src/yunohost/authenticators/ldap_admin.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +import os +import logging +import ldap +import ldap.sasl +import time +import ldap.modlist as modlist + +from moulinette import m18n +from moulinette.core import MoulinetteError +from moulinette.authentication import BaseAuthenticator +from yunohost.utils.error import YunohostError + +logger = logging.getLogger("yunohost.authenticators.lpda_admin") + +class Authenticator(BaseAuthenticator): + + """LDAP Authenticator + + Initialize a LDAP connexion for the given arguments. It attempts to + authenticate a user if 'user_rdn' is given - by associating user_rdn + and base_dn - and provides extra methods to manage opened connexion. + + Keyword arguments: + - uri -- The LDAP server URI + - base_dn -- The base dn + - user_rdn -- The user rdn to authenticate + + """ + + name = "ldap_admin" + + def __init__(self, *args, **kwargs): + self.uri = "ldap://localhost:389" + self.basedn = "dc=yunohost,dc=org" + self.admindn = "cn=admin,dc=yunohost,dc=org" + + def authenticate(self, password=None): + def _reconnect(): + con = ldap.ldapobject.ReconnectLDAPObject( + self.uri, retry_max=10, retry_delay=0.5 + ) + con.simple_bind_s(self.admindn, password) + return con + + try: + con = _reconnect() + except ldap.INVALID_CREDENTIALS: + raise MoulinetteError("invalid_password") + except ldap.SERVER_DOWN: + # ldap is down, attempt to restart it before really failing + logger.warning(m18n.g("ldap_server_is_down_restart_it")) + os.system("systemctl restart slapd") + time.sleep(10) # waits 10 secondes so we are sure that slapd has restarted + + try: + con = _reconnect() + except ldap.SERVER_DOWN: + raise YunohostError("ldap_server_down") + + # Check that we are indeed logged in with the expected identity + try: + # whoami_s return dn:..., then delete these 3 characters + who = con.whoami_s()[3:] + except Exception as e: + logger.warning("Error during ldap authentication process: %s", e) + raise + else: + if who != self.admindn: + raise MoulinetteError(f"Not logged with the appropriate identity ? Found {who}, expected {self.admindn} !?") + finally: + # Free the connection, we don't really need it to keep it open as the point is only to check authentication... + if con: + con.unbind_s() diff --git a/src/yunohost/utils/ldap.py b/src/yunohost/utils/ldap.py index 85bca34d7..28d7a17ce 100644 --- a/src/yunohost/utils/ldap.py +++ b/src/yunohost/utils/ldap.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - """ License Copyright (C) 2019 YunoHost @@ -21,10 +20,18 @@ import os import atexit -from moulinette.core import MoulinetteLdapIsDownError -from moulinette.authenticators import ldap +import logging +import ldap +import ldap.sasl +import time +import ldap.modlist as modlist + +from moulinette import m18n +from moulinette.core import MoulinetteError from yunohost.utils.error import YunohostError +logger = logging.getLogger("yunohost.utils.ldap") + # We use a global variable to do some caching # to avoid re-authenticating in case we call _get_ldap_authenticator multiple times _ldap_interface = None @@ -35,51 +42,21 @@ def _get_ldap_interface(): global _ldap_interface if _ldap_interface is None: - - conf = { - "vendor": "ldap", - "name": "as-root", - "parameters": { - "uri": "ldapi://%2Fvar%2Frun%2Fslapd%2Fldapi", - "base_dn": "dc=yunohost,dc=org", - "user_rdn": "gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth", - }, - "extra": {}, - } - - try: - _ldap_interface = ldap.Authenticator(**conf) - except MoulinetteLdapIsDownError: - raise YunohostError( - "Service slapd is not running but is required to perform this action ... You can try to investigate what's happening with 'systemctl status slapd'" - ) - - assert_slapd_is_running() + _ldap_interface = LDAPInterface() return _ldap_interface -def assert_slapd_is_running(): - - # Assert slapd is running... - if not os.system("pgrep slapd >/dev/null") == 0: - raise YunohostError( - "Service slapd is not running but is required to perform this action ... You can try to investigate what's happening with 'systemctl status slapd'" - ) - - # We regularly want to extract stuff like 'bar' in ldap path like # foo=bar,dn=users.example.org,ou=example.org,dc=org so this small helper allow # to do this without relying of dozens of mysterious string.split()[0] # # e.g. using _ldap_path_extract(path, "foo") on the previous example will # return bar - - def _ldap_path_extract(path, info): for element in path.split(","): if element.startswith(info + "="): - return element[len(info + "=") :] + return element[len(info + "="):] # Add this to properly close / delete the ldap interface / authenticator @@ -93,3 +70,247 @@ def _destroy_ldap_interface(): atexit.register(_destroy_ldap_interface) + + +class LDAPInterface(): + + def __init__(self): + logger.debug("initializing ldap interface") + + self.uri = "ldapi://%2Fvar%2Frun%2Fslapd%2Fldapi" + self.basedn = "dc=yunohost,dc=org" + self.rootdn = "gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" + self.connect() + + def connect(self): + def _reconnect(): + con = ldap.ldapobject.ReconnectLDAPObject( + self.uri, retry_max=10, retry_delay=0.5 + ) + con.sasl_non_interactive_bind_s("EXTERNAL") + return con + + try: + con = _reconnect() + except ldap.SERVER_DOWN: + # ldap is down, attempt to restart it before really failing + logger.warning(m18n.g("ldap_server_is_down_restart_it")) + os.system("systemctl restart slapd") + time.sleep(10) # waits 10 secondes so we are sure that slapd has restarted + try: + con = _reconnect() + except ldap.SERVER_DOWN: + raise YunohostError( + "Service slapd is not running but is required to perform this action ... " + "You can try to investigate what's happening with 'systemctl status slapd'" + ) + + # Check that we are indeed logged in with the right identity + try: + # whoami_s return dn:..., then delete these 3 characters + who = con.whoami_s()[3:] + except Exception as e: + logger.warning("Error during ldap authentication process: %s", e) + raise + else: + if who != self.rootdn: + raise MoulinetteError("Not logged in with the expected userdn ?!") + else: + self.con = con + + def __del__(self): + """Disconnect and free ressources""" + if hasattr(self, "con") and self.con: + self.con.unbind_s() + + def search(self, base=None, filter="(objectClass=*)", attrs=["dn"]): + """Search in LDAP base + + Perform an LDAP search operation with given arguments and return + results as a list. + + Keyword arguments: + - base -- The dn to search into + - filter -- A string representation of the filter to apply + - attrs -- A list of attributes to fetch + + Returns: + A list of all results + + """ + if not base: + base = self.basedn + + try: + result = self.con.search_s(base, ldap.SCOPE_SUBTREE, filter, attrs) + except Exception as e: + raise MoulinetteError( + "error during LDAP search operation with: base='%s', " + "filter='%s', attrs=%s and exception %s" % (base, filter, attrs, e), + raw_msg=True, + ) + + result_list = [] + if not attrs or "dn" not in attrs: + result_list = [entry for dn, entry in result] + else: + for dn, entry in result: + entry["dn"] = [dn] + result_list.append(entry) + + def decode(value): + if isinstance(value, bytes): + value = value.decode("utf-8") + return value + + # result_list is for example : + # [{'virtualdomain': [b'test.com']}, {'virtualdomain': [b'yolo.test']}, + for stuff in result_list: + if isinstance(stuff, dict): + for key, values in stuff.items(): + stuff[key] = [decode(v) for v in values] + + return result_list + + def add(self, rdn, attr_dict): + """ + Add LDAP entry + + Keyword arguments: + rdn -- DN without domain + attr_dict -- Dictionnary of attributes/values to add + + Returns: + Boolean | MoulinetteError + + """ + dn = rdn + "," + self.basedn + ldif = modlist.addModlist(attr_dict) + for i, (k, v) in enumerate(ldif): + if isinstance(v, list): + v = [a.encode("utf-8") for a in v] + elif isinstance(v, str): + v = [v.encode("utf-8")] + ldif[i] = (k, v) + + try: + self.con.add_s(dn, ldif) + except Exception as e: + raise MoulinetteError( + "error during LDAP add operation with: rdn='%s', " + "attr_dict=%s and exception %s" % (rdn, attr_dict, e), + raw_msg=True, + ) + else: + return True + + def remove(self, rdn): + """ + Remove LDAP entry + + Keyword arguments: + rdn -- DN without domain + + Returns: + Boolean | MoulinetteError + + """ + dn = rdn + "," + self.basedn + try: + self.con.delete_s(dn) + except Exception as e: + raise MoulinetteError( + "error during LDAP delete operation with: rdn='%s' and exception %s" + % (rdn, e), + raw_msg=True, + ) + else: + return True + + def update(self, rdn, attr_dict, new_rdn=False): + """ + Modify LDAP entry + + Keyword arguments: + rdn -- DN without domain + attr_dict -- Dictionnary of attributes/values to add + new_rdn -- New RDN for modification + + Returns: + Boolean | MoulinetteError + + """ + dn = rdn + "," + self.basedn + actual_entry = self.search(base=dn, attrs=None) + ldif = modlist.modifyModlist(actual_entry[0], attr_dict, ignore_oldexistent=1) + + if ldif == []: + logger.debug("Nothing to update in LDAP") + return True + + try: + if new_rdn: + self.con.rename_s(dn, new_rdn) + new_base = dn.split(",", 1)[1] + dn = new_rdn + "," + new_base + + for i, (a, k, vs) in enumerate(ldif): + if isinstance(vs, list): + vs = [v.encode("utf-8") for v in vs] + elif isinstance(vs, str): + vs = [vs.encode("utf-8")] + ldif[i] = (a, k, vs) + + self.con.modify_ext_s(dn, ldif) + except Exception as e: + raise MoulinetteError( + "error during LDAP update operation with: rdn='%s', " + "attr_dict=%s, new_rdn=%s and exception: %s" + % (rdn, attr_dict, new_rdn, e), + raw_msg=True, + ) + else: + return True + + def validate_uniqueness(self, value_dict): + """ + Check uniqueness of values + + Keyword arguments: + value_dict -- Dictionnary of attributes/values to check + + Returns: + Boolean | MoulinetteError + + """ + attr_found = self.get_conflict(value_dict) + if attr_found: + logger.info( + "attribute '%s' with value '%s' is not unique", + attr_found[0], + attr_found[1], + ) + raise MoulinetteError( + "ldap_attribute_already_exists", + attribute=attr_found[0], + value=attr_found[1], + ) + return True + + def get_conflict(self, value_dict, base_dn=None): + """ + Check uniqueness of values + + Keyword arguments: + value_dict -- Dictionnary of attributes/values to check + + Returns: + None | tuple with Fist conflict attribute name and value + + """ + for attr, value in value_dict.items(): + if not self.search(base=base_dn, filter=attr + "=" + value): + continue + else: + return (attr, value) + return None From 78cc445bd28bda590029270ba062da7360f49c10 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 9 Mar 2021 06:00:04 +0100 Subject: [PATCH 002/114] Typo --- src/yunohost/authenticators/ldap_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/authenticators/ldap_admin.py b/src/yunohost/authenticators/ldap_admin.py index d7a1dadda..dcecae88f 100644 --- a/src/yunohost/authenticators/ldap_admin.py +++ b/src/yunohost/authenticators/ldap_admin.py @@ -12,7 +12,7 @@ from moulinette.core import MoulinetteError from moulinette.authentication import BaseAuthenticator from yunohost.utils.error import YunohostError -logger = logging.getLogger("yunohost.authenticators.lpda_admin") +logger = logging.getLogger("yunohost.authenticators.ldap_admin") class Authenticator(BaseAuthenticator): From 727e135c728666a6e64b010d8051d050346faaa6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 10 Mar 2021 19:39:45 +0100 Subject: [PATCH 003/114] Unused import --- src/yunohost/authenticators/ldap_admin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yunohost/authenticators/ldap_admin.py b/src/yunohost/authenticators/ldap_admin.py index dcecae88f..734148536 100644 --- a/src/yunohost/authenticators/ldap_admin.py +++ b/src/yunohost/authenticators/ldap_admin.py @@ -5,7 +5,6 @@ import logging import ldap import ldap.sasl import time -import ldap.modlist as modlist from moulinette import m18n from moulinette.core import MoulinetteError From 913b02e4fcc32622a3a3763680333980c4c04028 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 24 Apr 2021 21:34:57 +0200 Subject: [PATCH 004/114] Add diagnosis section to check that app are in catalog with good quality + check for deprecated practices --- data/hooks/diagnosis/80-apps.py | 82 +++++++++++++++++++++++++++++++++ locales/en.json | 8 ++++ src/yunohost/app.py | 10 ++-- 3 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 data/hooks/diagnosis/80-apps.py diff --git a/data/hooks/diagnosis/80-apps.py b/data/hooks/diagnosis/80-apps.py new file mode 100644 index 000000000..ce54faef1 --- /dev/null +++ b/data/hooks/diagnosis/80-apps.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +import os +import re + +from yunohost.app import app_info, app_list +from moulinette.utils.filesystem import read_file + +from yunohost.settings import settings_get +from yunohost.diagnosis import Diagnoser +from yunohost.regenconf import _get_regenconf_infos, _calculate_hash +from moulinette.utils.filesystem import read_file + + +class AppDiagnoser(Diagnoser): + + id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] + cache_duration = 300 + dependencies = [] + + def run(self): + + apps = app_list(full=True)["apps"] + for app in apps: + app["issues"] = list(self.issues(app)) + + if not any(app["issues"] for app in apps): + yield dict( + meta={"test": "apps"}, + status="SUCCESS", + summary="diagnosis_apps_allgood", + ) + else: + for app in apps: + + if not app["issues"]: + continue + + level = "ERROR" if any(issue[0] == "error" for issue in app["issues"]) else "WARNING" + + yield dict( + meta={"test": "apps", "app": app["name"]}, + status=level, + summary="diagnosis_apps_issue", + details=[issue[1] for issue in app["issues"]] + ) + + def issues(self, app): + + # Check quality level in catalog + + if not app.get("from_catalog") or app["from_catalog"].get("state") != "working": + yield ("error", "diagnosis_apps_not_in_app_catalog") + elif not isinstance(app["from_catalog"].get("level"), int) or app["from_catalog"]["level"] == 0: + yield ("error", "diagnosis_apps_broken") + elif app["from_catalog"]["level"] <= 4: + yield ("warning", "diagnosis_apps_bad_quality") + + # Check for super old, deprecated practices + + yunohost_version_req = app["manifest"].get("requirements", {}).get("yunohost", "").strip(">= ") + if yunohost_version_req.startswith("2."): + yield ("error", "diagnosis_apps_outdated_ynh_requirement") + + deprecated_helpers = [ + "yunohost app setting", + "yunohost app checkurl", + "yunohost app checkport", + "yunohost app initdb", + "yunohost tools port-available", + ] + for deprecated_helper in deprecated_helpers: + if os.system(f"grep -nr -q '{deprecated_helper}' {app['setting_path']}/scripts/") == 0: + yield ("error", "diagnosis_apps_deprecated_practices") + + old_arg_regex = r'^domain=\${?[0-9]' + if os.system(f"grep -q '{old_arg_regex}' {app['setting_path']}/scripts/install") == 0: + yield ("error", "diagnosis_apps_deprecated_practices") + + +def main(args, env, loggers): + return AppDiagnoser(args, env, loggers).diagnose() diff --git a/locales/en.json b/locales/en.json index 938a38e20..8852f5587 100644 --- a/locales/en.json +++ b/locales/en.json @@ -248,6 +248,14 @@ "diagnosis_description_web": "Web", "diagnosis_description_mail": "Email", "diagnosis_description_regenconf": "System configurations", + "diagnosis_description_apps": "Applications", + "diagnosis_apps_allgood": "All installed apps respect basic packaging practices", + "diagnosis_apps_issue": "An issue was found for app {app}", + "diagnosis_apps_not_in_app_catalog": "This application is not in YunoHost's application catalog. If it was in the past and got removed, you should consider uninstalling this app as it won't receive upgrade, and may compromise the integrity and security of your system.", + "diagnosis_apps_broken": "This application is currently flagged as broken on YunoHost's application catalog. This may be a temporary issue while the maintainers attempt to fix the issue. In the meantime, upgrading this app is disabled.", + "diagnosis_apps_bad_quality": "This application is currently flagged as broken on YunoHost's application catalog. This may be a temporary issue while the maintainers attempt to fix the issue. In the meantime, upgrading this app is disabled.", + "diagnosis_apps_outdated_ynh_requirement": "This app's installed version only requires yunohost >= 2.x, which tends to indicate that it's not up to date with recommended packaging practices and helpers. You should really consider upgrading it.", + "diagnosis_apps_deprecated_practices": "This app's installed version still uses some super-old deprecated packaging practices. You should really consider upgrading it.", "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside in IPv{ipversion}.", "diagnosis_ports_could_not_diagnose_details": "Error: {error}", "diagnosis_ports_unreachable": "Port {port} is not reachable from outside.", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index c048ca5ea..646535fab 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -193,7 +193,8 @@ def app_info(app, full=False): "app_not_installed", app=app, all_apps=_get_all_installed_apps_id() ) - local_manifest = _get_manifest_of_app(os.path.join(APPS_SETTING_PATH, app)) + setting_path = os.path.join(APPS_SETTING_PATH, app) + local_manifest = _get_manifest_of_app(setting_path) permissions = user_permission_list(full=True, absolute_urls=True, apps=[app])[ "permissions" ] @@ -212,6 +213,7 @@ def app_info(app, full=False): if not full: return ret + ret["setting_path"] = setting_path ret["manifest"] = local_manifest ret["manifest"]["arguments"] = _set_default_ask_questions( ret["manifest"].get("arguments", {}) @@ -222,11 +224,11 @@ def app_info(app, full=False): ret["from_catalog"] = _load_apps_catalog()["apps"].get(absolute_app_name, {}) ret["upgradable"] = _app_upgradable(ret) ret["supports_change_url"] = os.path.exists( - os.path.join(APPS_SETTING_PATH, app, "scripts", "change_url") + os.path.join(setting_path, "scripts", "change_url") ) ret["supports_backup_restore"] = os.path.exists( - os.path.join(APPS_SETTING_PATH, app, "scripts", "backup") - ) and os.path.exists(os.path.join(APPS_SETTING_PATH, app, "scripts", "restore")) + os.path.join(setting_path, "scripts", "backup") + ) and os.path.exists(os.path.join(setting_path, "scripts", "restore")) ret["supports_multi_instance"] = is_true( local_manifest.get("multi_instance", False) ) From 7026a8cfd5cbc413cea9805298fafebf828f1366 Mon Sep 17 00:00:00 2001 From: Krakinou Date: Sat, 12 Jun 2021 16:49:05 +0200 Subject: [PATCH 005/114] Check home folder before creating multimedia directory --- data/helpers.d/multimedia | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/data/helpers.d/multimedia b/data/helpers.d/multimedia index 2d43c2540..c86153e85 100644 --- a/data/helpers.d/multimedia +++ b/data/helpers.d/multimedia @@ -31,7 +31,10 @@ ynh_multimedia_build_main_dir() { mkdir -p "$MEDIA_DIRECTORY/$user/eBook" ln -sfn "$MEDIA_DIRECTORY/share" "$MEDIA_DIRECTORY/$user/Share" # Création du lien symbolique dans le home de l'utilisateur. - ln -sfn "$MEDIA_DIRECTORY/$user" "/home/$user/Multimedia" + #link will only be created if the home directory of the user exists and if it's located in '/home' folder + if [[ -d "$(getent passwd $user | cut -d: -f6)" && "$(getent passwd $user | cut -d: -f6 | grep home)" ]]; then + ln -sfn "$MEDIA_DIRECTORY/$user" "/home/$user/Multimedia" + fi # Propriétaires des dossiers utilisateurs. chown -R $user "$MEDIA_DIRECTORY/$user" done From 075526303eff445b93ebdb1002a740abd77e29a1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 14 Jun 2021 17:00:41 +0200 Subject: [PATCH 006/114] Misc tweaks and fixes, port ldap auth test from moulinette --- debian/control | 1 + locales/en.json | 3 ++ src/yunohost/authenticators/ldap_admin.py | 20 ++------ src/yunohost/tests/test_ldap.py | 58 +++++++++++++++++++++++ src/yunohost/utils/ldap.py | 2 +- tests/test_i18n_keys.py | 1 + 6 files changed, 67 insertions(+), 18 deletions(-) create mode 100644 src/yunohost/tests/test_ldap.py diff --git a/debian/control b/debian/control index ef5061fe7..4c34d4c0a 100644 --- a/debian/control +++ b/debian/control @@ -14,6 +14,7 @@ Depends: ${python3:Depends}, ${misc:Depends} , python3-psutil, python3-requests, python3-dnspython, python3-openssl , python3-miniupnpc, python3-dbus, python3-jinja2 , python3-toml, python3-packaging, python3-publicsuffix + , python3-ldap , apt, apt-transport-https, apt-utils, dirmngr , php7.3-common, php7.3-fpm, php7.3-ldap, php7.3-intl , mariadb-server, php7.3-mysql diff --git a/locales/en.json b/locales/en.json index 199c21b66..4eba8c9f0 100644 --- a/locales/en.json +++ b/locales/en.json @@ -358,6 +358,8 @@ "invalid_regex": "Invalid regex:'{regex:s}'", "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", + "ldap_server_down": "Unable to reach LDAP server", + "ldap_server_is_down_restart_it": "The LDAP service is down, attempt to restart it...", "log_corrupted_md_file": "The YAML metadata file associated with logs is damaged: '{md_file}\nError: {error}'", "log_link_to_log": "Full log of this operation: '{desc}'", "log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log show {name}{name}'", @@ -470,6 +472,7 @@ "migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations run`.", "not_enough_disk_space": "Not enough free space on '{path:s}'", "invalid_number": "Must be a number", + "invalid_password": "Invalid password", "operation_interrupted": "The operation was manually interrupted?", "packages_upgrade_failed": "Could not upgrade all the packages", "password_listed": "This password is among the most used passwords in the world. Please choose something more unique.", diff --git a/src/yunohost/authenticators/ldap_admin.py b/src/yunohost/authenticators/ldap_admin.py index 734148536..47efe5bf9 100644 --- a/src/yunohost/authenticators/ldap_admin.py +++ b/src/yunohost/authenticators/ldap_admin.py @@ -7,7 +7,6 @@ import ldap.sasl import time from moulinette import m18n -from moulinette.core import MoulinetteError from moulinette.authentication import BaseAuthenticator from yunohost.utils.error import YunohostError @@ -15,19 +14,6 @@ logger = logging.getLogger("yunohost.authenticators.ldap_admin") class Authenticator(BaseAuthenticator): - """LDAP Authenticator - - Initialize a LDAP connexion for the given arguments. It attempts to - authenticate a user if 'user_rdn' is given - by associating user_rdn - and base_dn - and provides extra methods to manage opened connexion. - - Keyword arguments: - - uri -- The LDAP server URI - - base_dn -- The base dn - - user_rdn -- The user rdn to authenticate - - """ - name = "ldap_admin" def __init__(self, *args, **kwargs): @@ -46,10 +32,10 @@ class Authenticator(BaseAuthenticator): try: con = _reconnect() except ldap.INVALID_CREDENTIALS: - raise MoulinetteError("invalid_password") + raise YunohostError("invalid_password") except ldap.SERVER_DOWN: # ldap is down, attempt to restart it before really failing - logger.warning(m18n.g("ldap_server_is_down_restart_it")) + logger.warning(m18n.n("ldap_server_is_down_restart_it")) os.system("systemctl restart slapd") time.sleep(10) # waits 10 secondes so we are sure that slapd has restarted @@ -67,7 +53,7 @@ class Authenticator(BaseAuthenticator): raise else: if who != self.admindn: - raise MoulinetteError(f"Not logged with the appropriate identity ? Found {who}, expected {self.admindn} !?") + raise YunohostError(f"Not logged with the appropriate identity ? Found {who}, expected {self.admindn} !?", raw_msg=True) finally: # Free the connection, we don't really need it to keep it open as the point is only to check authentication... if con: diff --git a/src/yunohost/tests/test_ldap.py b/src/yunohost/tests/test_ldap.py new file mode 100644 index 000000000..b3a5efc09 --- /dev/null +++ b/src/yunohost/tests/test_ldap.py @@ -0,0 +1,58 @@ +import pytest +import os + +from yunohost.authenticators.ldap_admin import Authenticator as LDAPAuth +from yunohost.tools import tools_adminpw + +from moulinette import m18n +from moulinette.core import MoulinetteError + +def setup_function(function): + + if os.system("systemctl is-active slapd") != 0: + os.system("systemctl start slapd && sleep 3") + + tools_adminpw("yunohost", check_strength=False) + + +def test_authenticate(): + LDAPAuth().authenticate(password="yunohost") + + +def test_authenticate_with_wrong_password(): + with pytest.raises(MoulinetteError) as exception: + LDAPAuth().authenticate(password="bad_password_lul") + + translation = m18n.g("invalid_password") + expected_msg = translation.format() + assert expected_msg in str(exception) + + +def test_authenticate_server_down(mocker): + os.system("systemctl stop slapd && sleep 3") + + # Now if slapd is down, moulinette tries to restart it + mocker.patch("os.system") + mocker.patch("time.sleep") + with pytest.raises(MoulinetteError) as exception: + LDAPAuth().authenticate(password="yunohost") + + translation = m18n.n("ldap_server_down") + expected_msg = translation.format() + assert expected_msg in str(exception) + + +def test_authenticate_change_password(): + + LDAPAuth().authenticate(password="yunohost") + + tools_adminpw("plopette", check_strength=False) + + with pytest.raises(MoulinetteError) as exception: + LDAPAuth().authenticate(password="yunohost") + + translation = m18n.g("invalid_password") + expected_msg = translation.format() + assert expected_msg in str(exception) + + LDAPAuth().authenticate(password="plopette") diff --git a/src/yunohost/utils/ldap.py b/src/yunohost/utils/ldap.py index 28d7a17ce..1298eff69 100644 --- a/src/yunohost/utils/ldap.py +++ b/src/yunohost/utils/ldap.py @@ -94,7 +94,7 @@ class LDAPInterface(): con = _reconnect() except ldap.SERVER_DOWN: # ldap is down, attempt to restart it before really failing - logger.warning(m18n.g("ldap_server_is_down_restart_it")) + logger.warning(m18n.n("ldap_server_is_down_restart_it")) os.system("systemctl restart slapd") time.sleep(10) # waits 10 secondes so we are sure that slapd has restarted try: diff --git a/tests/test_i18n_keys.py b/tests/test_i18n_keys.py index 6876cbcd8..773a7f826 100644 --- a/tests/test_i18n_keys.py +++ b/tests/test_i18n_keys.py @@ -33,6 +33,7 @@ def find_expected_string_keys(): python_files = glob.glob("src/yunohost/*.py") python_files.extend(glob.glob("src/yunohost/utils/*.py")) python_files.extend(glob.glob("src/yunohost/data_migrations/*.py")) + python_files.extend(glob.glob("src/yunohost/authenticators/*.py")) python_files.extend(glob.glob("data/hooks/diagnosis/*.py")) python_files.append("bin/yunohost") From 52920cc52f777143875a3435e9d46a36d514db05 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 14 Jun 2021 17:31:08 +0200 Subject: [PATCH 007/114] password -> credentials, pave the way to multi-admins --- src/yunohost/authenticators/ldap_admin.py | 8 ++++++-- src/yunohost/tests/test_ldap.py | 12 ++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/yunohost/authenticators/ldap_admin.py b/src/yunohost/authenticators/ldap_admin.py index 47efe5bf9..808497b31 100644 --- a/src/yunohost/authenticators/ldap_admin.py +++ b/src/yunohost/authenticators/ldap_admin.py @@ -21,12 +21,16 @@ class Authenticator(BaseAuthenticator): self.basedn = "dc=yunohost,dc=org" self.admindn = "cn=admin,dc=yunohost,dc=org" - def authenticate(self, password=None): + def authenticate(self, credentials=None): + + # TODO : change authentication format + # to support another dn to support multi-admins + def _reconnect(): con = ldap.ldapobject.ReconnectLDAPObject( self.uri, retry_max=10, retry_delay=0.5 ) - con.simple_bind_s(self.admindn, password) + con.simple_bind_s(self.admindn, credentials) return con try: diff --git a/src/yunohost/tests/test_ldap.py b/src/yunohost/tests/test_ldap.py index b3a5efc09..0ad8366c9 100644 --- a/src/yunohost/tests/test_ldap.py +++ b/src/yunohost/tests/test_ldap.py @@ -16,12 +16,12 @@ def setup_function(function): def test_authenticate(): - LDAPAuth().authenticate(password="yunohost") + LDAPAuth().authenticate(credentials="yunohost") def test_authenticate_with_wrong_password(): with pytest.raises(MoulinetteError) as exception: - LDAPAuth().authenticate(password="bad_password_lul") + LDAPAuth().authenticate(credentials="bad_password_lul") translation = m18n.g("invalid_password") expected_msg = translation.format() @@ -35,7 +35,7 @@ def test_authenticate_server_down(mocker): mocker.patch("os.system") mocker.patch("time.sleep") with pytest.raises(MoulinetteError) as exception: - LDAPAuth().authenticate(password="yunohost") + LDAPAuth().authenticate(credentials="yunohost") translation = m18n.n("ldap_server_down") expected_msg = translation.format() @@ -44,15 +44,15 @@ def test_authenticate_server_down(mocker): def test_authenticate_change_password(): - LDAPAuth().authenticate(password="yunohost") + LDAPAuth().authenticate(credentials="yunohost") tools_adminpw("plopette", check_strength=False) with pytest.raises(MoulinetteError) as exception: - LDAPAuth().authenticate(password="yunohost") + LDAPAuth().authenticate(credentials="yunohost") translation = m18n.g("invalid_password") expected_msg = translation.format() assert expected_msg in str(exception) - LDAPAuth().authenticate(password="plopette") + LDAPAuth().authenticate(credentials="plopette") From 1fcfe734e96616c39b49681c1ba3d8db8dedf1c5 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 14 Jun 2021 18:23:55 +0200 Subject: [PATCH 008/114] Add test-ldapauth to ci --- .gitlab/ci/test.gitlab-ci.yml | 9 +++++++++ src/yunohost/tests/{test_ldap.py => test_ldapauth.py} | 0 2 files changed, 9 insertions(+) rename src/yunohost/tests/{test_ldap.py => test_ldapauth.py} (100%) diff --git a/.gitlab/ci/test.gitlab-ci.yml b/.gitlab/ci/test.gitlab-ci.yml index f146442e4..a9e14b6e4 100644 --- a/.gitlab/ci/test.gitlab-ci.yml +++ b/.gitlab/ci/test.gitlab-ci.yml @@ -181,3 +181,12 @@ test-service: only: changes: - src/yunohost/service.py + +test-ldapauth: + extends: .test-stage + script: + - cd src/yunohost + - python3 -m pytest tests/test_ldapauth.py + only: + changes: + - src/yunohost/authenticators/*.py diff --git a/src/yunohost/tests/test_ldap.py b/src/yunohost/tests/test_ldapauth.py similarity index 100% rename from src/yunohost/tests/test_ldap.py rename to src/yunohost/tests/test_ldapauth.py From 9c23f4390550a1c0161880ee0d9bbad6d62a6e90 Mon Sep 17 00:00:00 2001 From: Krakinou Date: Mon, 14 Jun 2021 23:28:31 +0200 Subject: [PATCH 009/114] check if home exists --- data/helpers.d/multimedia | 5 +++-- data/hooks/post_user_create/ynh_multimedia | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/data/helpers.d/multimedia b/data/helpers.d/multimedia index c86153e85..a07f5fdfd 100644 --- a/data/helpers.d/multimedia +++ b/data/helpers.d/multimedia @@ -32,8 +32,9 @@ ynh_multimedia_build_main_dir() { ln -sfn "$MEDIA_DIRECTORY/share" "$MEDIA_DIRECTORY/$user/Share" # Création du lien symbolique dans le home de l'utilisateur. #link will only be created if the home directory of the user exists and if it's located in '/home' folder - if [[ -d "$(getent passwd $user | cut -d: -f6)" && "$(getent passwd $user | cut -d: -f6 | grep home)" ]]; then - ln -sfn "$MEDIA_DIRECTORY/$user" "/home/$user/Multimedia" + home="$(getent passwd $user | cut -d: -f6)" + if [[ -d "$home" && "$(echo "$home" | grep home)" ]]; then + ln -sfn "$MEDIA_DIRECTORY/$user" "$home/Multimedia" fi # Propriétaires des dossiers utilisateurs. chown -R $user "$MEDIA_DIRECTORY/$user" diff --git a/data/hooks/post_user_create/ynh_multimedia b/data/hooks/post_user_create/ynh_multimedia index 441212bbc..aa5ab48d3 100644 --- a/data/hooks/post_user_create/ynh_multimedia +++ b/data/hooks/post_user_create/ynh_multimedia @@ -15,7 +15,11 @@ mkdir -p "$MEDIA_DIRECTORY/$user/Video" mkdir -p "$MEDIA_DIRECTORY/$user/eBook" ln -sfn "$MEDIA_DIRECTORY/share" "$MEDIA_DIRECTORY/$user/Share" # Création du lien symbolique dans le home de l'utilisateur. -ln -sfn "$MEDIA_DIRECTORY/$user" "/home/$user/Multimedia" +#link will only be created if the home directory of the user exists and if it's located in '/home' folder +home="$(getent passwd $user | cut -d: -f6)" +if [[ -d "$home" && "$(echo "$home" | grep home)" ]]; then + ln -sfn "$MEDIA_DIRECTORY/$user" "$home/Multimedia" +fi # Propriétaires des dossiers utilisateurs. chown -R $user "$MEDIA_DIRECTORY/$user" From 29db3d51fff28e1c9bf1a2e129e1fcb1353d2c5b Mon Sep 17 00:00:00 2001 From: Krakinou Date: Mon, 14 Jun 2021 23:37:30 +0200 Subject: [PATCH 010/114] grep full /home/ --- data/helpers.d/multimedia | 2 +- data/hooks/post_user_create/ynh_multimedia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/multimedia b/data/helpers.d/multimedia index a07f5fdfd..4ec7611fc 100644 --- a/data/helpers.d/multimedia +++ b/data/helpers.d/multimedia @@ -33,7 +33,7 @@ ynh_multimedia_build_main_dir() { # Création du lien symbolique dans le home de l'utilisateur. #link will only be created if the home directory of the user exists and if it's located in '/home' folder home="$(getent passwd $user | cut -d: -f6)" - if [[ -d "$home" && "$(echo "$home" | grep home)" ]]; then + if [[ -d "$home" && "$(echo "$home" | grep /home/)" ]]; then ln -sfn "$MEDIA_DIRECTORY/$user" "$home/Multimedia" fi # Propriétaires des dossiers utilisateurs. diff --git a/data/hooks/post_user_create/ynh_multimedia b/data/hooks/post_user_create/ynh_multimedia index aa5ab48d3..2fa02505a 100644 --- a/data/hooks/post_user_create/ynh_multimedia +++ b/data/hooks/post_user_create/ynh_multimedia @@ -17,7 +17,7 @@ ln -sfn "$MEDIA_DIRECTORY/share" "$MEDIA_DIRECTORY/$user/Share" # Création du lien symbolique dans le home de l'utilisateur. #link will only be created if the home directory of the user exists and if it's located in '/home' folder home="$(getent passwd $user | cut -d: -f6)" -if [[ -d "$home" && "$(echo "$home" | grep home)" ]]; then +if [[ -d "$home" && "$(echo "$home" | grep /home/)" ]]; then ln -sfn "$MEDIA_DIRECTORY/$user" "$home/Multimedia" fi # Propriétaires des dossiers utilisateurs. From c3e75877662422decedcafafc6acbdee5e473b95 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 19 Jun 2021 17:17:08 +0200 Subject: [PATCH 011/114] Propagate change from the moulinette: authenticate -> _authenticate_credentials --- src/yunohost/authenticators/ldap_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/authenticators/ldap_admin.py b/src/yunohost/authenticators/ldap_admin.py index 808497b31..dd6eec03e 100644 --- a/src/yunohost/authenticators/ldap_admin.py +++ b/src/yunohost/authenticators/ldap_admin.py @@ -21,7 +21,7 @@ class Authenticator(BaseAuthenticator): self.basedn = "dc=yunohost,dc=org" self.admindn = "cn=admin,dc=yunohost,dc=org" - def authenticate(self, credentials=None): + def _authenticate_credentials(self, credentials=None): # TODO : change authentication format # to support another dn to support multi-admins From 5e2478d3096a480c0f8190b02be5c7010c104ea9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 9 Jul 2021 20:43:52 +0200 Subject: [PATCH 012/114] Propagate change from the moulinette (no more msignals madness) --- data/actionsmap/yunohost.yml | 1 + src/yunohost/app.py | 8 ++++---- src/yunohost/backup.py | 6 +++--- src/yunohost/domain.py | 4 ++-- src/yunohost/tools.py | 6 +++--- src/yunohost/user.py | 10 +++++----- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 604034019..d2c4f8470 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -33,6 +33,7 @@ # Global parameters # ############################# _global: + name: yunohost.admin authentication: api: ldap_admin cli: null diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 5f001c12a..ac2b95cac 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -36,7 +36,7 @@ import urllib.parse import tempfile from collections import OrderedDict -from moulinette import msignals, m18n, msettings +from moulinette import prompt, m18n, msettings from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger from moulinette.utils.network import download_json @@ -825,7 +825,7 @@ def app_install( return if confirm in ["danger", "thirdparty"]: - answer = msignals.prompt( + answer = prompt( m18n.n("confirm_app_install_" + confirm, answers="Yes, I understand"), color="red", ) @@ -833,7 +833,7 @@ def app_install( raise YunohostError("aborting") else: - answer = msignals.prompt( + answer = prompt( m18n.n("confirm_app_install_" + confirm, answers="Y/N"), color="yellow" ) if answer.upper() != "Y": @@ -2729,7 +2729,7 @@ class YunoHostArgumentFormatParser(object): ) try: - question.value = msignals.prompt( + question.value = prompt( text_for_user_input_in_cli, self.hide_user_input_in_prompt ) except NotImplementedError: diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 99337b2f8..b76d12aba 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -38,7 +38,7 @@ from collections import OrderedDict from functools import reduce from packaging import version -from moulinette import msignals, m18n, msettings +from moulinette import prompt, m18n, msettings from moulinette.utils import filesystem from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_file, mkdir, write_to_yaml, read_yaml @@ -1839,7 +1839,7 @@ class BackupMethod(object): # Ask confirmation for copying if size > MB_ALLOWED_TO_ORGANIZE: try: - i = msignals.prompt( + i = prompt( m18n.n( "backup_ask_for_copying_if_needed", answers="y/N", @@ -2343,7 +2343,7 @@ def backup_restore(name, system=[], apps=[], force=False): if not force: try: # Ask confirmation for restoring - i = msignals.prompt( + i = prompt( m18n.n("restore_confirm_yunohost_installed", answers="y/N") ) except NotImplemented: diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index aaac3a995..c466e14ee 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -26,7 +26,7 @@ import os import re -from moulinette import m18n, msettings, msignals +from moulinette import m18n, msettings, prompt from moulinette.core import MoulinetteError from yunohost.utils.error import YunohostError, YunohostValidationError from moulinette.utils.log import getActionLogger @@ -237,7 +237,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False): if apps_on_that_domain: if remove_apps: if msettings.get("interface") == "cli" and not force: - answer = msignals.prompt( + answer = prompt( m18n.n( "domain_remove_confirm_apps_removal", apps="\n".join([x[1] for x in apps_on_that_domain]), diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 1cd197d70..4c5861d17 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -30,7 +30,7 @@ import time from importlib import import_module from packaging import version -from moulinette import msignals, m18n +from moulinette import prompt, m18n from moulinette.utils.log import getActionLogger from moulinette.utils.process import check_output, call_async_output from moulinette.utils.filesystem import read_yaml, write_to_yaml @@ -692,7 +692,7 @@ def tools_shutdown(operation_logger, force=False): if not shutdown: try: # Ask confirmation for server shutdown - i = msignals.prompt(m18n.n("server_shutdown_confirm", answers="y/N")) + i = prompt(m18n.n("server_shutdown_confirm", answers="y/N")) except NotImplemented: pass else: @@ -711,7 +711,7 @@ def tools_reboot(operation_logger, force=False): if not reboot: try: # Ask confirmation for restoring - i = msignals.prompt(m18n.n("server_reboot_confirm", answers="y/N")) + i = prompt(m18n.n("server_reboot_confirm", answers="y/N")) except NotImplemented: pass else: diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 266c2774c..0a624c4b3 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -33,7 +33,7 @@ import string import subprocess import copy -from moulinette import msignals, msettings, m18n +from moulinette import prompt, display, msettings, m18n from moulinette.utils.log import getActionLogger from moulinette.utils.process import check_output @@ -123,12 +123,12 @@ def user_create( ) else: # On affiche les differents domaines possibles - msignals.display(m18n.n("domains_available")) + display(m18n.n("domains_available")) for domain in domain_list()["domains"]: - msignals.display("- {}".format(domain)) + display("- {}".format(domain)) maindomain = _get_maindomain() - domain = msignals.prompt( + domain = prompt( m18n.n("ask_user_domain") + " (default: %s)" % maindomain ) if not domain: @@ -380,7 +380,7 @@ def user_update( # without a specified value, change_password will be set to the const 0. # In this case we prompt for the new password. if msettings.get("interface") == "cli" and not change_password: - change_password = msignals.prompt(m18n.n("ask_password"), True, True) + change_password = prompt(m18n.n("ask_password"), True, True) # Ensure sufficiently complex password assert_password_is_strong_enough("user", change_password) From a2009d6a9a792a3c2a89eaeaf9a1751b18ea0f6a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 9 Jul 2021 21:49:12 +0200 Subject: [PATCH 013/114] Propagate changes from moulinette : get rid of msettings, and prompt/display are also wrapped in a 'Moulinette' object --- src/yunohost/app.py | 14 +++++++------- src/yunohost/backup.py | 10 +++++----- src/yunohost/diagnosis.py | 8 ++++---- src/yunohost/domain.py | 8 ++++---- src/yunohost/hook.py | 4 ++-- src/yunohost/log.py | 11 +++++------ src/yunohost/tests/conftest.py | 6 ++++-- src/yunohost/tools.py | 6 +++--- src/yunohost/user.py | 14 +++++++------- 9 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index ac2b95cac..fc890e055 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -36,7 +36,7 @@ import urllib.parse import tempfile from collections import OrderedDict -from moulinette import prompt, m18n, msettings +from moulinette import Moulinette, m18n from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger from moulinette.utils.network import download_json @@ -643,7 +643,7 @@ def app_upgrade(app=[], url=None, file=None, force=False): m18n.n("app_upgrade_failed", app=app_instance_name, error=error) ) failure_message_with_debug_instructions = operation_logger.error(error) - if msettings.get("interface") != "api": + if Moulinette.interface.type != "api": dump_app_log_extract_for_debugging(operation_logger) # Script got manually interrupted ... N.B. : KeyboardInterrupt does not inherit from Exception except (KeyboardInterrupt, EOFError): @@ -821,11 +821,11 @@ def app_install( def confirm_install(confirm): # Ignore if there's nothing for confirm (good quality app), if --force is used # or if request on the API (confirm already implemented on the API side) - if confirm is None or force or msettings.get("interface") == "api": + if confirm is None or force or Moulinette.interface.type == "api": return if confirm in ["danger", "thirdparty"]: - answer = prompt( + answer = Moulinette.prompt( m18n.n("confirm_app_install_" + confirm, answers="Yes, I understand"), color="red", ) @@ -833,7 +833,7 @@ def app_install( raise YunohostError("aborting") else: - answer = prompt( + answer = Moulinette.prompt( m18n.n("confirm_app_install_" + confirm, answers="Y/N"), color="yellow" ) if answer.upper() != "Y": @@ -1005,7 +1005,7 @@ def app_install( error = m18n.n("app_install_script_failed") logger.error(m18n.n("app_install_failed", app=app_id, error=error)) failure_message_with_debug_instructions = operation_logger.error(error) - if msettings.get("interface") != "api": + if Moulinette.interface.type != "api": dump_app_log_extract_for_debugging(operation_logger) # Script got manually interrupted ... N.B. : KeyboardInterrupt does not inherit from Exception except (KeyboardInterrupt, EOFError): @@ -2729,7 +2729,7 @@ class YunoHostArgumentFormatParser(object): ) try: - question.value = prompt( + question.value = Moulinette.prompt( text_for_user_input_in_cli, self.hide_user_input_in_prompt ) except NotImplementedError: diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index b76d12aba..2fe32ad7e 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -38,7 +38,7 @@ from collections import OrderedDict from functools import reduce from packaging import version -from moulinette import prompt, m18n, msettings +from moulinette import Moulinette, m18n from moulinette.utils import filesystem from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_file, mkdir, write_to_yaml, read_yaml @@ -1508,7 +1508,7 @@ class RestoreManager: m18n.n("app_restore_failed", app=app_instance_name, error=error) ) failure_message_with_debug_instructions = operation_logger.error(error) - if msettings.get("interface") != "api": + if Moulinette.interface.type != "api": dump_app_log_extract_for_debugging(operation_logger) # Script got manually interrupted ... N.B. : KeyboardInterrupt does not inherit from Exception except (KeyboardInterrupt, EOFError): @@ -1839,7 +1839,7 @@ class BackupMethod(object): # Ask confirmation for copying if size > MB_ALLOWED_TO_ORGANIZE: try: - i = prompt( + i = Moulinette.prompt( m18n.n( "backup_ask_for_copying_if_needed", answers="y/N", @@ -2343,7 +2343,7 @@ def backup_restore(name, system=[], apps=[], force=False): if not force: try: # Ask confirmation for restoring - i = prompt( + i = Moulinette.prompt( m18n.n("restore_confirm_yunohost_installed", answers="y/N") ) except NotImplemented: @@ -2417,7 +2417,7 @@ def backup_list(with_info=False, human_readable=False): def backup_download(name): - if msettings.get("interface") != "api": + if Moulinette.interface.type != "api": logger.error( "This option is only meant for the API/webadmin and doesn't make sense for the command line." ) diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index ff1a14c4e..4ac5e2731 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -28,7 +28,7 @@ import re import os import time -from moulinette import m18n, msettings +from moulinette import m18n, Moulinette from moulinette.utils import log from moulinette.utils.filesystem import ( read_json, @@ -138,7 +138,7 @@ def diagnosis_show( url = yunopaste(content) logger.info(m18n.n("log_available_on_yunopaste", url=url)) - if msettings.get("interface") == "api": + if Moulinette.interface.type == "api": return {"url": url} else: return @@ -219,7 +219,7 @@ def diagnosis_run( if email: _email_diagnosis_issues() - if issues and msettings.get("interface") == "cli": + if issues and Moulinette.interface.type == "cli": logger.warning(m18n.n("diagnosis_display_tip")) @@ -595,7 +595,7 @@ class Diagnoser: info[1].update(meta_data) s = m18n.n(info[0], **(info[1])) # In cli, we remove the html tags - if msettings.get("interface") != "api" or force_remove_html_tags: + if Moulinette.interface.type != "api" or force_remove_html_tags: s = s.replace("", "'").replace("", "'") s = html_tags.sub("", s.replace("
", "\n")) else: diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index c466e14ee..b431e9b18 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -26,7 +26,7 @@ import os import re -from moulinette import m18n, msettings, prompt +from moulinette import m18n, Moulinette from moulinette.core import MoulinetteError from yunohost.utils.error import YunohostError, YunohostValidationError from moulinette.utils.log import getActionLogger @@ -236,8 +236,8 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False): if apps_on_that_domain: if remove_apps: - if msettings.get("interface") == "cli" and not force: - answer = prompt( + if Moulinette.interface.type == "cli" and not force: + answer = Moulinette.prompt( m18n.n( "domain_remove_confirm_apps_removal", apps="\n".join([x[1] for x in apps_on_that_domain]), @@ -343,7 +343,7 @@ def domain_dns_conf(domain, ttl=None): for record in record_list: result += "\n{name} {ttl} IN {type} {value}".format(**record) - if msettings.get("interface") == "cli": + if Moulinette.interface.type == "cli": logger.info(m18n.n("domain_dns_conf_is_just_a_recommendation")) return result diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 493ad2c35..e46a43d35 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -31,7 +31,7 @@ import mimetypes from glob import iglob from importlib import import_module -from moulinette import m18n, msettings +from moulinette import m18n, Moulinette from yunohost.utils.error import YunohostError, YunohostValidationError from moulinette.utils import log from moulinette.utils.filesystem import read_json @@ -409,7 +409,7 @@ def _hook_exec_bash(path, args, chdir, env, return_format, loggers): env = {} env["YNH_CWD"] = chdir - env["YNH_INTERFACE"] = msettings.get("interface") + env["YNH_INTERFACE"] = Moulinette.interface.type stdreturn = os.path.join(tempfile.mkdtemp(), "stdreturn") with open(stdreturn, "w") as f: diff --git a/src/yunohost/log.py b/src/yunohost/log.py index d36671ce2..f6c19eebc 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -33,7 +33,7 @@ import psutil from datetime import datetime, timedelta from logging import FileHandler, getLogger, Formatter -from moulinette import m18n, msettings +from moulinette import m18n, Moulinette from moulinette.core import MoulinetteError from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.packages import get_ynh_package_version @@ -44,7 +44,6 @@ CATEGORIES_PATH = "/var/log/yunohost/categories/" OPERATIONS_PATH = "/var/log/yunohost/categories/operation/" METADATA_FILE_EXT = ".yml" LOG_FILE_EXT = ".log" -RELATED_CATEGORIES = ["app", "domain", "group", "service", "user"] logger = getActionLogger("yunohost.log") @@ -125,7 +124,7 @@ def log_list(limit=None, with_details=False, with_suboperations=False): operations = list(reversed(sorted(operations, key=lambda o: o["name"]))) # Reverse the order of log when in cli, more comfortable to read (avoid # unecessary scrolling) - is_api = msettings.get("interface") == "api" + is_api = Moulinette.interface.type == "api" if not is_api: operations = list(reversed(operations)) @@ -214,7 +213,7 @@ def log_show( url = yunopaste(content) logger.info(m18n.n("log_available_on_yunopaste", url=url)) - if msettings.get("interface") == "api": + if Moulinette.interface.type == "api": return {"url": url} else: return @@ -609,7 +608,7 @@ class OperationLogger(object): "operation": self.operation, "parent": self.parent, "yunohost_version": get_ynh_package_version("yunohost")["version"], - "interface": msettings.get("interface"), + "interface": Moulinette.interface.type, } if self.related_to is not None: data["related_to"] = self.related_to @@ -663,7 +662,7 @@ class OperationLogger(object): self.logger.removeHandler(self.file_handler) self.file_handler.close() - is_api = msettings.get("interface") == "api" + is_api = Moulinette.interface.type == "api" desc = _get_description_from_name(self.name) if error is None: if is_api: diff --git a/src/yunohost/tests/conftest.py b/src/yunohost/tests/conftest.py index 49f87decf..1bf035748 100644 --- a/src/yunohost/tests/conftest.py +++ b/src/yunohost/tests/conftest.py @@ -3,7 +3,7 @@ import pytest import sys import moulinette -from moulinette import m18n, msettings +from moulinette import m18n, Moulinette from yunohost.utils.error import YunohostError from contextlib import contextmanager @@ -81,4 +81,6 @@ def pytest_cmdline_main(config): import yunohost yunohost.init(debug=config.option.yunodebug) - msettings["interface"] = "test" + class DummyInterface(): + type = "test" + Moulinette._interface = DummyInterface() diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 4c5861d17..4190e7614 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -30,7 +30,7 @@ import time from importlib import import_module from packaging import version -from moulinette import prompt, m18n +from moulinette import Moulinette, m18n from moulinette.utils.log import getActionLogger from moulinette.utils.process import check_output, call_async_output from moulinette.utils.filesystem import read_yaml, write_to_yaml @@ -692,7 +692,7 @@ def tools_shutdown(operation_logger, force=False): if not shutdown: try: # Ask confirmation for server shutdown - i = prompt(m18n.n("server_shutdown_confirm", answers="y/N")) + i = Moulinette.prompt(m18n.n("server_shutdown_confirm", answers="y/N")) except NotImplemented: pass else: @@ -711,7 +711,7 @@ def tools_reboot(operation_logger, force=False): if not reboot: try: # Ask confirmation for restoring - i = prompt(m18n.n("server_reboot_confirm", answers="y/N")) + i = Moulinette.prompt(m18n.n("server_reboot_confirm", answers="y/N")) except NotImplemented: pass else: diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 0a624c4b3..01513f3bd 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -33,7 +33,7 @@ import string import subprocess import copy -from moulinette import prompt, display, msettings, m18n +from moulinette import Moulinette, m18n from moulinette.utils.log import getActionLogger from moulinette.utils.process import check_output @@ -117,18 +117,18 @@ def user_create( # Validate domain used for email address/xmpp account if domain is None: - if msettings.get("interface") == "api": + if Moulinette.interface.type == "api": raise YunohostValidationError( "Invalid usage, you should specify a domain argument" ) else: # On affiche les differents domaines possibles - display(m18n.n("domains_available")) + Moulinette.display(m18n.n("domains_available")) for domain in domain_list()["domains"]: - display("- {}".format(domain)) + Moulinette.display("- {}".format(domain)) maindomain = _get_maindomain() - domain = prompt( + domain = Moulinette.prompt( m18n.n("ask_user_domain") + " (default: %s)" % maindomain ) if not domain: @@ -379,8 +379,8 @@ def user_update( # when in the cli interface if the option to change the password is called # without a specified value, change_password will be set to the const 0. # In this case we prompt for the new password. - if msettings.get("interface") == "cli" and not change_password: - change_password = prompt(m18n.n("ask_password"), True, True) + if Moulinette.interface.type == "cli" and not change_password: + change_password = Moulinette.prompt(m18n.n("ask_password"), True, True) # Ensure sufficiently complex password assert_password_is_strong_enough("user", change_password) From 7c92b44ddb19b3c712f78b898e11a5fadc9b7092 Mon Sep 17 00:00:00 2001 From: tituspijean Date: Mon, 5 Jul 2021 21:24:00 +0000 Subject: [PATCH 014/114] Add mdns.py for mDNS broadcast --- data/other/mdns.py | 94 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 data/other/mdns.py diff --git a/data/other/mdns.py b/data/other/mdns.py new file mode 100644 index 000000000..144742758 --- /dev/null +++ b/data/other/mdns.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 + +""" +WIP +Pythonic declaration of mDNS .local domains. +Heavily based off https://github.com/jstasiak/python-zeroconf/blob/master/tests/test_asyncio.py +""" + +import os +import sys +import argparse + +import asyncio +import logging +import socket +import time +from typing import List + +sys.path.insert(0, "/usr/lib/moulinette/") +from yunohost.domain import domain_list + +from zeroconf import IPVersion +from zeroconf.asyncio import AsyncServiceInfo, AsyncZeroconf + +# TODO: Remove traceback beautification +from rich.traceback import install +install(show_locals=True) + +async def register_services(infos: List[AsyncServiceInfo]) -> None: + tasks = [aiozc.async_register_service(info) for info in infos] + background_tasks = await asyncio.gather(*tasks) + await asyncio.gather(*background_tasks) + + +async def unregister_services(infos: List[AsyncServiceInfo]) -> None: + tasks = [aiozc.async_unregister_service(info) for info in infos] + background_tasks = await asyncio.gather(*tasks) + await asyncio.gather(*background_tasks) + + +async def close_aiozc(aiozc: AsyncZeroconf) -> None: + await aiozc.async_close() + + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + + local_domains = [ d for d in domain_list()['domains'] if d.endswith('.local') ] + + parser = argparse.ArgumentParser() + parser.add_argument('--debug', action='store_true') + version_group = parser.add_mutually_exclusive_group() + version_group.add_argument('--v6', action='store_true') + version_group.add_argument('--v6-only', action='store_true') + args = parser.parse_args() + + if args.debug: + logging.getLogger('zeroconf').setLevel(logging.DEBUG) + if args.v6: + ip_version = IPVersion.All + elif args.v6_only: + ip_version = IPVersion.V6Only + else: + ip_version = IPVersion.V4Only + + infos = [] + for d in local_domains: + d_domain=d.replace('.local','') + infos.append( + AsyncServiceInfo( + type_="_device-info._tcp.local.", + name=d_domain+f"._device-info._tcp.local.", + addresses=[socket.inet_aton("127.0.0.1")], + port=80, + server=d, + ) + ) + + print("Registration of .local domains, press Ctrl-C to exit...") + aiozc = AsyncZeroconf(ip_version=ip_version) + loop = asyncio.get_event_loop() + loop.run_until_complete(register_services(infos)) + print("Registration complete.") + try: + while True: + time.sleep(0.1) + except KeyboardInterrupt: + pass + finally: + print("Unregistering...") + loop.run_until_complete(unregister_services(infos)) + print("Unregistration complete.") + loop.run_until_complete(close_aiozc(aiozc)) + From 99390f23133f41b21a2665dbccbb417bbdc46413 Mon Sep 17 00:00:00 2001 From: tituspijean Date: Mon, 5 Jul 2021 23:01:19 +0000 Subject: [PATCH 015/114] PoC for mDNS broadcast --- data/other/mdns.py | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/data/other/mdns.py b/data/other/mdns.py index 144742758..2146caec0 100644 --- a/data/other/mdns.py +++ b/data/other/mdns.py @@ -2,8 +2,8 @@ """ WIP -Pythonic declaration of mDNS .local domains. -Heavily based off https://github.com/jstasiak/python-zeroconf/blob/master/tests/test_asyncio.py +Pythonic declaration of mDNS .local domains for YunoHost +Based off https://github.com/jstasiak/python-zeroconf/blob/master/tests/test_asyncio.py """ import os @@ -18,6 +18,7 @@ from typing import List sys.path.insert(0, "/usr/lib/moulinette/") from yunohost.domain import domain_list +from yunohost.utils.network import get_network_interfaces from zeroconf import IPVersion from zeroconf.asyncio import AsyncServiceInfo, AsyncZeroconf @@ -47,21 +48,26 @@ if __name__ == '__main__': local_domains = [ d for d in domain_list()['domains'] if d.endswith('.local') ] + # TODO: Create setting to list interfaces + wanted_interfaces = [ 'zt3jnskpna' ] + interfaces = get_network_interfaces() + ips = [] + for i in wanted_interfaces: + try: + ips.append(socket.inet_pton(socket.AF_INET, interfaces[i]['ipv4'].split('/')[0])) + except: + pass + try: + ips.append(socket.inet_pton(socket.AF_INET6, interfaces[i]['ipv6'].split('/')[0])) + except: + pass + parser = argparse.ArgumentParser() parser.add_argument('--debug', action='store_true') - version_group = parser.add_mutually_exclusive_group() - version_group.add_argument('--v6', action='store_true') - version_group.add_argument('--v6-only', action='store_true') args = parser.parse_args() if args.debug: logging.getLogger('zeroconf').setLevel(logging.DEBUG) - if args.v6: - ip_version = IPVersion.All - elif args.v6_only: - ip_version = IPVersion.V6Only - else: - ip_version = IPVersion.V4Only infos = [] for d in local_domains: @@ -70,14 +76,14 @@ if __name__ == '__main__': AsyncServiceInfo( type_="_device-info._tcp.local.", name=d_domain+f"._device-info._tcp.local.", - addresses=[socket.inet_aton("127.0.0.1")], + addresses=ips, port=80, - server=d, + server=d+'.', ) ) print("Registration of .local domains, press Ctrl-C to exit...") - aiozc = AsyncZeroconf(ip_version=ip_version) + aiozc = AsyncZeroconf() loop = asyncio.get_event_loop() loop.run_until_complete(register_services(infos)) print("Registration complete.") From 654690b42749ea0ee022136f601ccc8c171e13e6 Mon Sep 17 00:00:00 2001 From: tituspijean Date: Tue, 6 Jul 2021 10:48:39 +0000 Subject: [PATCH 016/114] Add interfaces selection for mDNS broadcast - System setting added - Loop through list of interfaces --- data/other/mdns.py | 106 +++++++++++++++++++++++---------------- locales/en.json | 1 + src/yunohost/settings.py | 1 + 3 files changed, 65 insertions(+), 43 deletions(-) diff --git a/data/other/mdns.py b/data/other/mdns.py index 2146caec0..0e249e7b8 100644 --- a/data/other/mdns.py +++ b/data/other/mdns.py @@ -19,26 +19,23 @@ from typing import List sys.path.insert(0, "/usr/lib/moulinette/") from yunohost.domain import domain_list from yunohost.utils.network import get_network_interfaces +from yunohost.settings import settings_get +from moulinette import m18n +from moulinette.interfaces.cli import get_locale -from zeroconf import IPVersion from zeroconf.asyncio import AsyncServiceInfo, AsyncZeroconf -# TODO: Remove traceback beautification -from rich.traceback import install -install(show_locals=True) -async def register_services(infos: List[AsyncServiceInfo]) -> None: +async def register_services(aiozc: AsyncZeroconf, infos: List[AsyncServiceInfo]) -> None: tasks = [aiozc.async_register_service(info) for info in infos] background_tasks = await asyncio.gather(*tasks) await asyncio.gather(*background_tasks) - -async def unregister_services(infos: List[AsyncServiceInfo]) -> None: +async def unregister_services(aiozc: AsyncZeroconf, infos: List[AsyncServiceInfo]) -> None: tasks = [aiozc.async_unregister_service(info) for info in infos] background_tasks = await asyncio.gather(*tasks) await asyncio.gather(*background_tasks) - async def close_aiozc(aiozc: AsyncZeroconf) -> None: await aiozc.async_close() @@ -46,22 +43,6 @@ async def close_aiozc(aiozc: AsyncZeroconf) -> None: if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) - local_domains = [ d for d in domain_list()['domains'] if d.endswith('.local') ] - - # TODO: Create setting to list interfaces - wanted_interfaces = [ 'zt3jnskpna' ] - interfaces = get_network_interfaces() - ips = [] - for i in wanted_interfaces: - try: - ips.append(socket.inet_pton(socket.AF_INET, interfaces[i]['ipv4'].split('/')[0])) - except: - pass - try: - ips.append(socket.inet_pton(socket.AF_INET6, interfaces[i]['ipv6'].split('/')[0])) - except: - pass - parser = argparse.ArgumentParser() parser.add_argument('--debug', action='store_true') args = parser.parse_args() @@ -69,24 +50,62 @@ if __name__ == '__main__': if args.debug: logging.getLogger('zeroconf').setLevel(logging.DEBUG) - infos = [] - for d in local_domains: - d_domain=d.replace('.local','') - infos.append( - AsyncServiceInfo( - type_="_device-info._tcp.local.", - name=d_domain+f"._device-info._tcp.local.", - addresses=ips, - port=80, - server=d+'.', - ) - ) - print("Registration of .local domains, press Ctrl-C to exit...") - aiozc = AsyncZeroconf() - loop = asyncio.get_event_loop() - loop.run_until_complete(register_services(infos)) - print("Registration complete.") + local_domains = [ d for d in domain_list()['domains'] if d.endswith('.local') ] + + m18n.load_namespace("yunohost") + m18n.set_locale(get_locale()) + + if settings_get('mdns.interfaces'): + wanted_interfaces = settings_get('mdns.interfaces').split() + else: + wanted_interfaces = [] + print('No interface listed for broadcast.') + + aiozcs = [] + interfaces = get_network_interfaces() + for interface in wanted_interfaces: + infos = [] + ips = [] # Human-readable IPs + b_ips = [] # Binary-convered IPs + + # Parse the IPs and prepare their binary version + try: + ip = interfaces[interface]['ipv4'].split('/')[0] + ips.append(ip) + b_ips.append(socket.inet_pton(socket.AF_INET, ip)) + except: + pass + try: + ip = interfaces[interface]['ipv6'].split('/')[0] + ips.append(ip) + b_ips.append(socket.inet_pton(socket.AF_INET6, ip)) + except: + pass + + # Create a ServiceInfo object for each .local domain + for d in local_domains: + d_domain=d.replace('.local','') + infos.append( + AsyncServiceInfo( + type_="_device-info._tcp.local.", + name=d_domain+f"._device-info._tcp.local.", + addresses=b_ips, + port=80, + server=d+'.', + ) + ) + print('Adding '+d+' with addresses '+str(ips)+' on interface '+interface) + + # Create an AsyncZeroconf object, store it, and start Service registration + aiozc = AsyncZeroconf(interfaces=ips) + aiozcs.append(aiozc) + print("Registration on interface "+interface+"...") + loop = asyncio.get_event_loop() + loop.run_until_complete(register_services(aiozc, infos)) + + # We are done looping among the interfaces + print("Registration complete. Press Ctrl-c to exit...") try: while True: time.sleep(0.1) @@ -94,7 +113,8 @@ if __name__ == '__main__': pass finally: print("Unregistering...") - loop.run_until_complete(unregister_services(infos)) + for aiozc in aiozcs: + loop.run_until_complete(unregister_services(aiozc, infos)) + loop.run_until_complete(close_aiozc(aiozc)) print("Unregistration complete.") - loop.run_until_complete(close_aiozc(aiozc)) diff --git a/locales/en.json b/locales/en.json index 84a01dfaa..70a0e9309 100644 --- a/locales/en.json +++ b/locales/en.json @@ -321,6 +321,7 @@ "global_settings_cant_write_settings": "Could not save settings file, reason: {reason:s}", "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_setting_mdns_interfaces": "Space-separated list of interfaces for mDNS broadcast. Leave empty to disable mDNS.", "global_settings_setting_pop3_enabled": "Enable the POP3 protocol for the mail server", "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", diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index 0466d8126..36904ee70 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -100,6 +100,7 @@ DEFAULTS = OrderedDict( ("smtp.relay.password", {"type": "string", "default": ""}), ("backup.compress_tar_archives", {"type": "bool", "default": False}), ("ssowat.panel_overlay.enabled", {"type": "bool", "default": True}), + ("mdns.interfaces", {"type": "string", "default": ""}), ] ) From 9e93efa895ae6c3a7013c47adeb2891bdf3c8a3f Mon Sep 17 00:00:00 2001 From: tituspijean Date: Tue, 6 Jul 2021 20:33:23 +0000 Subject: [PATCH 017/114] Move mDNS script to yunomdns --- data/other/mdns.py => bin/yunomdns | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename data/other/mdns.py => bin/yunomdns (100%) mode change 100644 => 100755 diff --git a/data/other/mdns.py b/bin/yunomdns old mode 100644 new mode 100755 similarity index 100% rename from data/other/mdns.py rename to bin/yunomdns From 99aacd8b51ade29f5f153cde3808f55706c15bf6 Mon Sep 17 00:00:00 2001 From: tituspijean Date: Sun, 11 Jul 2021 14:23:41 +0000 Subject: [PATCH 018/114] Do not rely on Moulinette, integration with Yunohost of mDNS broadcast --- bin/yunomdns | 269 +++++++++++++++++++++------ data/hooks/conf_regen/01-yunohost | 5 +- data/other/yunomdns.service | 13 ++ data/templates/yunohost/mdns.yml | 4 + data/templates/yunohost/services.yml | 2 + debian/install | 1 + debian/postinst | 4 + 7 files changed, 244 insertions(+), 54 deletions(-) create mode 100644 data/other/yunomdns.service create mode 100644 data/templates/yunohost/mdns.yml diff --git a/bin/yunomdns b/bin/yunomdns index 0e249e7b8..795481dfa 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -6,115 +6,278 @@ Pythonic declaration of mDNS .local domains for YunoHost Based off https://github.com/jstasiak/python-zeroconf/blob/master/tests/test_asyncio.py """ +import subprocess import os +import re import sys import argparse +import yaml import asyncio import logging import socket import time -from typing import List - -sys.path.insert(0, "/usr/lib/moulinette/") -from yunohost.domain import domain_list -from yunohost.utils.network import get_network_interfaces -from yunohost.settings import settings_get -from moulinette import m18n -from moulinette.interfaces.cli import get_locale +from typing import List, Dict +from zeroconf import DNSEntry, DNSRecord from zeroconf.asyncio import AsyncServiceInfo, AsyncZeroconf +# Helper command taken from Moulinette +def check_output(args, stderr=subprocess.STDOUT, shell=True, **kwargs): + """Run command with arguments and return its output as a byte string + Overwrite some of the arguments to capture standard error in the result + and use shell by default before calling subprocess.check_output. + """ + return ( + subprocess.check_output(args, stderr=stderr, shell=shell, **kwargs) + .decode("utf-8") + .strip() + ) -async def register_services(aiozc: AsyncZeroconf, infos: List[AsyncServiceInfo]) -> None: - tasks = [aiozc.async_register_service(info) for info in infos] +# Helper command taken from Moulinette +def _extract_inet(string, skip_netmask=False, skip_loopback=True): + """ + Extract IP addresses (v4 and/or v6) from a string limited to one + address by protocol + + Keyword argument: + string -- String to search in + skip_netmask -- True to skip subnet mask extraction + skip_loopback -- False to include addresses reserved for the + loopback interface + + Returns: + A dict of {protocol: address} with protocol one of 'ipv4' or 'ipv6' + + """ + ip4_pattern = ( + r"((25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}" + ) + ip6_pattern = r"(((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)" + ip4_pattern += r"/[0-9]{1,2})" if not skip_netmask else ")" + ip6_pattern += r"/[0-9]{1,3})" if not skip_netmask else ")" + result = {} + + for m in re.finditer(ip4_pattern, string): + addr = m.group(1) + if skip_loopback and addr.startswith("127."): + continue + + # Limit to only one result + result["ipv4"] = addr + break + + for m in re.finditer(ip6_pattern, string): + addr = m.group(1) + if skip_loopback and addr == "::1": + continue + + # Limit to only one result + result["ipv6"] = addr + break + + return result + +# Helper command taken from Moulinette +def get_network_interfaces(): + # Get network devices and their addresses (raw infos from 'ip addr') + devices_raw = {} + output = check_output("ip addr show") + for d in re.split(r"^(?:[0-9]+: )", output, flags=re.MULTILINE): + # Extract device name (1) and its addresses (2) + m = re.match(r"([^\s@]+)(?:@[\S]+)?: (.*)", d, flags=re.DOTALL) + if m: + devices_raw[m.group(1)] = m.group(2) + + # Parse relevant informations for each of them + devices = { + name: _extract_inet(addrs) + for name, addrs in devices_raw.items() + if name != "lo" + } + + return devices + +# async commands +async def register_services(aiozcs: Dict[AsyncZeroconf, List[AsyncServiceInfo]]) -> None: + tasks = [] + for aiozc, infos in aiozcs.items(): + for info in infos: + tasks.append(aiozc.async_register_service(info)) background_tasks = await asyncio.gather(*tasks) await asyncio.gather(*background_tasks) -async def unregister_services(aiozc: AsyncZeroconf, infos: List[AsyncServiceInfo]) -> None: - tasks = [aiozc.async_unregister_service(info) for info in infos] +async def unregister_services(aiozcs: Dict[AsyncZeroconf, List[AsyncServiceInfo]]) -> None: + for aiozc, infos in aiozcs.items(): + for info in infos: + tasks.append(aiozc.async_unregister_service(info)) background_tasks = await asyncio.gather(*tasks) await asyncio.gather(*background_tasks) -async def close_aiozc(aiozc: AsyncZeroconf) -> None: - await aiozc.async_close() +async def close_aiozcs(aiozcs: Dict[AsyncZeroconf, List[AsyncServiceInfo]]) -> None: + tasks = [] + for aiozc in aiozcs: + tasks.append(aiozc.async_close()) + background_tasks = await asyncio.gather(*tasks) + await asyncio.gather(*background_tasks) if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) - parser = argparse.ArgumentParser() + + ### + # ARGUMENTS + ### + + parser = argparse.ArgumentParser(description=''' + mDNS broadcast for .local domains. + Configuration file: /etc/yunohost/mdns.yml + Subdomains are not supported. + ''') parser.add_argument('--debug', action='store_true') + parser.add_argument('--regen', nargs='?', const='as_stored', choices=['domains', 'interfaces', 'all', 'as_stored'], + help=''' + Regenerates selection into the configuration file then starts mDNS broadcasting. + ''') + parser.add_argument('--set-regen', choices=['domains', 'interfaces', 'all', 'none'], + help=''' + Set which part of the configuration to be regenerated. + Implies --regen as_stored, with newly stored parameter. + ''') + able = parser.add_mutually_exclusive_group() + able.add_argument('--enable', action='store_true', help='Enables mDNS broadcast, and regenerates the configuration') + able.add_argument('--disable', action='store_true') args = parser.parse_args() if args.debug: logging.getLogger('zeroconf').setLevel(logging.DEBUG) - - - local_domains = [ d for d in domain_list()['domains'] if d.endswith('.local') ] - - m18n.load_namespace("yunohost") - m18n.set_locale(get_locale()) - - if settings_get('mdns.interfaces'): - wanted_interfaces = settings_get('mdns.interfaces').split() + logging.getLogger('asyncio').setLevel(logging.DEBUG) else: - wanted_interfaces = [] - print('No interface listed for broadcast.') + logging.getLogger('zeroconf').setLevel(logging.WARNING) + logging.getLogger('asyncio').setLevel(logging.WARNING) - aiozcs = [] + ### + # CONFIG + ### + + with open('/etc/yunohost/mdns.yml', 'r') as f: + config = yaml.load(f) or {} + updated = False + + if args.enable: + config['enabled'] = True + args.regen = 'as_stored' + updated = True + + if args.disable: + config['enabled'] = False + updated = True + + if args.set_regen: + config['regen'] = args.set_regen + args.regen = 'as_stored' + updated = True + + if args.regen: + if args.regen == 'as_stored': + r = config['regen'] + else: + r = args.regen + if r == 'none': + print('Regeneration disabled.') + if r == 'interfaces' or r == 'all': + config['interfaces'] = [ i for i in get_network_interfaces() ] + print('Regenerated interfaces list: ' + str(config['interfaces'])) + if r == 'domains' or r == 'all': + import glob + config['domains'] = [ d.rsplit('/',1)[1][:-2] for d in glob.glob('/etc/nginx/conf.d/*.local.d') ] + print('Regenerated domains list: ' + str(config['domains'])) + updated = True + + if updated: + with open('/etc/yunohost/mdns.yml', 'w') as f: + yaml.safe_dump(config, f, default_flow_style=False) + print('Configuration file updated.') + + ### + # MAIN SCRIPT + ### + + if config['enabled'] is not True: + print('YunomDNS is disabled.') + sys.exit(0) + + if config['interfaces'] is None: + print('No interface listed for broadcast.') + sys.exit(0) + + aiozcs = {} + tasks = [] + loop = asyncio.get_event_loop() interfaces = get_network_interfaces() - for interface in wanted_interfaces: - infos = [] + for interface in config['interfaces']: + infos = [] # List of ServiceInfo objects, to feed Zeroconf ips = [] # Human-readable IPs b_ips = [] # Binary-convered IPs # Parse the IPs and prepare their binary version + addressed = False try: ip = interfaces[interface]['ipv4'].split('/')[0] + if len(ip)>0: addressed = True ips.append(ip) b_ips.append(socket.inet_pton(socket.AF_INET, ip)) except: pass try: ip = interfaces[interface]['ipv6'].split('/')[0] + if len(ip)>0: addressed = True ips.append(ip) b_ips.append(socket.inet_pton(socket.AF_INET6, ip)) except: pass - # Create a ServiceInfo object for each .local domain - for d in local_domains: - d_domain=d.replace('.local','') - infos.append( - AsyncServiceInfo( - type_="_device-info._tcp.local.", - name=d_domain+f"._device-info._tcp.local.", - addresses=b_ips, - port=80, - server=d+'.', + # If at least one IP is listed + if addressed: + # Create a ServiceInfo object for each .local domain + for d in config['domains']: + d_domain=d.replace('.local','') + infos.append( + AsyncServiceInfo( + type_='_device-info._tcp.local.', + name=d_domain+'._device-info._tcp.local.', + addresses=b_ips, + port=80, + server=d+'.', + ) ) - ) - print('Adding '+d+' with addresses '+str(ips)+' on interface '+interface) + infos.append( + AsyncServiceInfo( + type_='_http._tcp.local.', + name=d_domain+'._http._tcp.local.', + addresses=b_ips, + port=80, + server=d+'.', + ) + ) + print('Adding '+d+' with addresses '+str(ips)+' on interface '+interface) + # Create an AsyncZeroconf object, and store registration task + aiozc = AsyncZeroconf(interfaces=ips) + aiozcs[aiozc]=infos - # Create an AsyncZeroconf object, store it, and start Service registration - aiozc = AsyncZeroconf(interfaces=ips) - aiozcs.append(aiozc) - print("Registration on interface "+interface+"...") - loop = asyncio.get_event_loop() - loop.run_until_complete(register_services(aiozc, infos)) + # Run registration + loop.run_until_complete(register_services(aiozcs)) + print("Registration complete. Press Ctrl-c or stop service to exit...") - # We are done looping among the interfaces - print("Registration complete. Press Ctrl-c to exit...") try: while True: - time.sleep(0.1) + time.sleep(1) except KeyboardInterrupt: pass finally: print("Unregistering...") - for aiozc in aiozcs: - loop.run_until_complete(unregister_services(aiozc, infos)) - loop.run_until_complete(close_aiozc(aiozc)) + loop.run_until_complete(unregister_services(aiozcs)) print("Unregistration complete.") + loop.run_until_complete(close_aiozcs(aiozcs)) diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index 3d65d34cd..d160b9e66 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -3,6 +3,7 @@ set -e services_path="/etc/yunohost/services.yml" +mdns_path="/etc/yunohost/mdns.yml" do_init_regen() { if [[ $EUID -ne 0 ]]; then @@ -18,9 +19,11 @@ do_init_regen() { [[ -f /etc/yunohost/current_host ]] \ || echo "yunohost.org" > /etc/yunohost/current_host - # copy default services and firewall + # copy default services, mdns, and firewall [[ -f $services_path ]] \ || cp services.yml "$services_path" + [[ -f $mdns_path ]] \ + || cp mdns.yml "$mdns_path" [[ -f /etc/yunohost/firewall.yml ]] \ || cp firewall.yml /etc/yunohost/firewall.yml diff --git a/data/other/yunomdns.service b/data/other/yunomdns.service new file mode 100644 index 000000000..36d938035 --- /dev/null +++ b/data/other/yunomdns.service @@ -0,0 +1,13 @@ +[Unit] +Description=YunoHost mDNS service +After=network.target + +[Service] +User=avahi +Group=avahi +Type=simple +ExecStart=/usr/bin/yunomdns +StandardOutput=syslog + +[Install] +WantedBy=default.target diff --git a/data/templates/yunohost/mdns.yml b/data/templates/yunohost/mdns.yml new file mode 100644 index 000000000..3ed9e792b --- /dev/null +++ b/data/templates/yunohost/mdns.yml @@ -0,0 +1,4 @@ +enabled: True +regen: all +interfaces: +domains: diff --git a/data/templates/yunohost/services.yml b/data/templates/yunohost/services.yml index 7df563c67..b961d274e 100644 --- a/data/templates/yunohost/services.yml +++ b/data/templates/yunohost/services.yml @@ -52,6 +52,8 @@ yunohost-firewall: need_lock: true test_status: iptables -S | grep "^-A INPUT" | grep " --dport" | grep -q ACCEPT category: security +yunomdns: + needs_exposed_ports: [5353] glances: null nsswitch: null ssl: null diff --git a/debian/install b/debian/install index 1691a4849..e30a69a8b 100644 --- a/debian/install +++ b/debian/install @@ -5,6 +5,7 @@ doc/yunohost.8.gz /usr/share/man/man8/ data/actionsmap/* /usr/share/moulinette/actionsmap/ data/hooks/* /usr/share/yunohost/hooks/ data/other/yunoprompt.service /etc/systemd/system/ +data/other/yunomdns.service /etc/systemd/system/ data/other/password/* /usr/share/yunohost/other/password/ data/other/dpkg-origins/yunohost /etc/dpkg/origins data/other/dnsbl_list.yml /usr/share/yunohost/other/ diff --git a/debian/postinst b/debian/postinst index ecae9b258..7590197bd 100644 --- a/debian/postinst +++ b/debian/postinst @@ -38,6 +38,10 @@ do_configure() { # Yunoprompt systemctl enable yunoprompt.service + + # Yunomdns + chown avahi:avahi /etc/yunohost/mdns.yml + systemctl enable yunomdns.service } # summary of how this script can be called: From 842783f64c54bc0744d1692f5b1e4ea37e7c35ce Mon Sep 17 00:00:00 2001 From: tituspijean Date: Sun, 11 Jul 2021 15:20:14 +0000 Subject: [PATCH 019/114] Accomodate mDNS feature in diagnosis --- data/hooks/diagnosis/12-dnsrecords.py | 23 +++++++++++++++++++++-- data/hooks/diagnosis/21-web.py | 6 ++++++ data/templates/yunohost/services.yml | 1 + locales/en.json | 4 +++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index 719ce4d6a..89816847d 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -29,8 +29,9 @@ class DNSRecordsDiagnoser(Diagnoser): for domain in all_domains: self.logger_debug("Diagnosing DNS conf for %s" % domain) is_subdomain = domain.split(".", 1)[1] in all_domains + is_localdomain = domain.endswith(".local") for report in self.check_domain( - domain, domain == main_domain, is_subdomain=is_subdomain + domain, domain == main_domain, is_subdomain=is_subdomain, is_localdomain=is_localdomain ): yield report @@ -48,7 +49,7 @@ class DNSRecordsDiagnoser(Diagnoser): for report in self.check_expiration_date(domains_from_registrar): yield report - def check_domain(self, domain, is_main_domain, is_subdomain): + def check_domain(self, domain, is_main_domain, is_subdomain, is_localdomain): expected_configuration = _build_dns_conf( domain, include_empty_AAAA_if_no_ipv6=True @@ -59,6 +60,24 @@ class DNSRecordsDiagnoser(Diagnoser): if is_subdomain: categories = ["basic"] + if is_localdomain: + categories = [] + if is_subdomain: + yield dict( + meta={"domain": domain, "category": "basic"}, + results={}, + status="WARNING", + summary="diagnosis_domain_subdomain_localdomain", + ) + else: + yield dict( + meta={"domain": domain, "category": "basic"}, + results={}, + status="INFO", + summary="diagnosis_domain_localdomain", + ) + + for category in categories: records = expected_configuration[category] diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index 81c4d6e48..04c36661e 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -34,6 +34,12 @@ class WebDiagnoser(Diagnoser): summary="diagnosis_http_nginx_conf_not_up_to_date", details=["diagnosis_http_nginx_conf_not_up_to_date_details"], ) + elif domain.endswith('.local'): + yield dict( + meta={"domain": domain}, + status="INFO", + summary="diagnosis_http_localdomain", + ) else: domains_to_check.append(domain) diff --git a/data/templates/yunohost/services.yml b/data/templates/yunohost/services.yml index b961d274e..447829684 100644 --- a/data/templates/yunohost/services.yml +++ b/data/templates/yunohost/services.yml @@ -54,6 +54,7 @@ yunohost-firewall: category: security yunomdns: needs_exposed_ports: [5353] + category: mdns glances: null nsswitch: null ssl: null diff --git a/locales/en.json b/locales/en.json index 70a0e9309..3734b7cf3 100644 --- a/locales/en.json +++ b/locales/en.json @@ -190,6 +190,8 @@ "diagnosis_domain_expiration_warning": "Some domains will expire soon!", "diagnosis_domain_expiration_error": "Some domains will expire VERY SOON!", "diagnosis_domain_expires_in": "{domain} expires in {days} days.", + "diagnosis_domain_localdomain": "Domain {domain}, with a .local TLD, is not expected to have DNS records as it can be discovered through mDNS.", + "diagnosis_domain_subdomain_localdomain": "Domain {domain} is a subdomain of a .local domain. Zeroconf/mDNS discovery only works with first-level domains.", "diagnosis_services_running": "Service {service} is running!", "diagnosis_services_conf_broken": "Configuration is broken for service {service}!", "diagnosis_services_bad_status": "Service {service} is {status} :(", @@ -259,6 +261,7 @@ "diagnosis_http_hairpinning_issue_details": "This is probably because of your ISP box / router. As a result, people from outside your local network will be able to access your server as expected, but not people from inside the local network (like you, probably?) when using the domain name or global IP. You may be able to improve the situation by having a look at https://yunohost.org/dns_local_network", "diagnosis_http_could_not_diagnose": "Could not diagnose if domains are reachable from outside in IPv{ipversion}.", "diagnosis_http_could_not_diagnose_details": "Error: {error}", + "diagnosis_http_localdomain": "Domain {domain}, with a .local TLD, is not expected to be reached from outside the local network.", "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that port 80 (and 443) are not correctly forwarded to your server.
2. You should also make sure that the service nginx is running
3. On more complex setups: make sure that no firewall or reverse-proxy is interfering.", "diagnosis_http_connection_error": "Connection error: could not connect to the requested domain, it's very likely unreachable.", @@ -321,7 +324,6 @@ "global_settings_cant_write_settings": "Could not save settings file, reason: {reason:s}", "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_setting_mdns_interfaces": "Space-separated list of interfaces for mDNS broadcast. Leave empty to disable mDNS.", "global_settings_setting_pop3_enabled": "Enable the POP3 protocol for the mail server", "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", From f14dcec711f1856373456033134ba4ffe019c13e Mon Sep 17 00:00:00 2001 From: tituspijean Date: Sun, 11 Jul 2021 15:20:35 +0000 Subject: [PATCH 020/114] Remove mDNS setting now we use our own setting file, not a system setting --- src/yunohost/settings.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index 36904ee70..0466d8126 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -100,7 +100,6 @@ DEFAULTS = OrderedDict( ("smtp.relay.password", {"type": "string", "default": ""}), ("backup.compress_tar_archives", {"type": "bool", "default": False}), ("ssowat.panel_overlay.enabled", {"type": "bool", "default": True}), - ("mdns.interfaces", {"type": "string", "default": ""}), ] ) From 92a1a12226d6caa5becb502bc2c0c690da4c199c Mon Sep 17 00:00:00 2001 From: tituspijean Date: Sun, 11 Jul 2021 17:32:33 +0000 Subject: [PATCH 021/114] [mdns] Fix NonUniqueNameException Service names have to be unique even accross different interfaces --- bin/yunomdns | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index 795481dfa..ad1c1a012 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -246,7 +246,7 @@ if __name__ == '__main__': infos.append( AsyncServiceInfo( type_='_device-info._tcp.local.', - name=d_domain+'._device-info._tcp.local.', + name=interface+' '+d_domain+'._device-info._tcp.local.', addresses=b_ips, port=80, server=d+'.', @@ -255,7 +255,7 @@ if __name__ == '__main__': infos.append( AsyncServiceInfo( type_='_http._tcp.local.', - name=d_domain+'._http._tcp.local.', + name=interface+' '+d_domain+'._http._tcp.local.', addresses=b_ips, port=80, server=d+'.', From 7c58aa198c1096f4ce27993f7f6a05f628445dae Mon Sep 17 00:00:00 2001 From: tituspijean Date: Tue, 27 Jul 2021 20:38:02 +0000 Subject: [PATCH 022/114] Remove asyncio and do not support subdomains --- bin/yunomdns | 88 +++++++++++++++++----------------------------------- 1 file changed, 28 insertions(+), 60 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index ad1c1a012..123935871 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -16,11 +16,10 @@ import yaml import asyncio import logging import socket -import time +from time import sleep from typing import List, Dict -from zeroconf import DNSEntry, DNSRecord -from zeroconf.asyncio import AsyncServiceInfo, AsyncZeroconf +from zeroconf import Zeroconf, ServiceInfo # Helper command taken from Moulinette def check_output(args, stderr=subprocess.STDOUT, shell=True, **kwargs): @@ -98,30 +97,6 @@ def get_network_interfaces(): return devices -# async commands -async def register_services(aiozcs: Dict[AsyncZeroconf, List[AsyncServiceInfo]]) -> None: - tasks = [] - for aiozc, infos in aiozcs.items(): - for info in infos: - tasks.append(aiozc.async_register_service(info)) - background_tasks = await asyncio.gather(*tasks) - await asyncio.gather(*background_tasks) - -async def unregister_services(aiozcs: Dict[AsyncZeroconf, List[AsyncServiceInfo]]) -> None: - for aiozc, infos in aiozcs.items(): - for info in infos: - tasks.append(aiozc.async_unregister_service(info)) - background_tasks = await asyncio.gather(*tasks) - await asyncio.gather(*background_tasks) - -async def close_aiozcs(aiozcs: Dict[AsyncZeroconf, List[AsyncServiceInfo]]) -> None: - tasks = [] - for aiozc in aiozcs: - tasks.append(aiozc.async_close()) - background_tasks = await asyncio.gather(*tasks) - await asyncio.gather(*background_tasks) - - if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) @@ -212,9 +187,7 @@ if __name__ == '__main__': print('No interface listed for broadcast.') sys.exit(0) - aiozcs = {} - tasks = [] - loop = asyncio.get_event_loop() + zcs = {} interfaces = get_network_interfaces() for interface in config['interfaces']: infos = [] # List of ServiceInfo objects, to feed Zeroconf @@ -240,44 +213,39 @@ if __name__ == '__main__': # If at least one IP is listed if addressed: - # Create a ServiceInfo object for each .local domain + # Create a Zeroconf object, and store the ServiceInfos + zc = Zeroconf(interfaces=ips) + zcs[zc]=[] for d in config['domains']: d_domain=d.replace('.local','') - infos.append( - AsyncServiceInfo( - type_='_device-info._tcp.local.', - name=interface+' '+d_domain+'._device-info._tcp.local.', - addresses=b_ips, - port=80, - server=d+'.', - ) - ) - infos.append( - AsyncServiceInfo( - type_='_http._tcp.local.', - name=interface+' '+d_domain+'._http._tcp.local.', - addresses=b_ips, - port=80, - server=d+'.', - ) - ) - print('Adding '+d+' with addresses '+str(ips)+' on interface '+interface) - # Create an AsyncZeroconf object, and store registration task - aiozc = AsyncZeroconf(interfaces=ips) - aiozcs[aiozc]=infos + if '.' in d_domain: + print(d_domain+'.local: subdomains are not supported.') + else: + # Create a ServiceInfo object for each .local domain + zcs[zc].append(ServiceInfo( + type_='_device-info._tcp.local.', + name=interface+': '+d_domain+'._device-info._tcp.local.', + addresses=b_ips, + port=80, + server=d+'.', + )) + print('Adding '+d+' with addresses '+str(ips)+' on interface '+interface) # Run registration - loop.run_until_complete(register_services(aiozcs)) - print("Registration complete. Press Ctrl-c or stop service to exit...") + print("Registering...") + for zc, infos in zcs.items(): + for info in infos: + zc.register_service(info) try: + print("Registered. Press Ctrl+C or stop service to stop.") while True: - time.sleep(1) + sleep(1) except KeyboardInterrupt: pass finally: print("Unregistering...") - loop.run_until_complete(unregister_services(aiozcs)) - print("Unregistration complete.") - loop.run_until_complete(close_aiozcs(aiozcs)) - + for zc, infos in zcs.items(): + for info in infos: + zc.unregister_service(info) + zc.close() From c4762c05eb55e08f5f3cd19adc54bd1a7f91ab8b Mon Sep 17 00:00:00 2001 From: Luca Date: Sun, 8 Aug 2021 21:45:57 +0000 Subject: [PATCH 023/114] Translated using Weblate (German) Currently translated at 100.0% (637 of 637 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/locales/de.json b/locales/de.json index 1ef86980a..76de18746 100644 --- a/locales/de.json +++ b/locales/de.json @@ -124,7 +124,7 @@ "upnp_dev_not_found": "Es konnten keine UPnP Geräte gefunden werden", "upnp_disabled": "UPnP deaktiviert", "upnp_enabled": "UPnP aktiviert", - "upnp_port_open_failed": "UPnP Port konnte nicht geöffnet werden.", + "upnp_port_open_failed": "Port konnte nicht via UPnP geöffnet werden", "user_created": "Benutzer erstellt", "user_creation_failed": "Benutzer konnte nicht erstellt werden {user}: {error}", "user_deleted": "Benutzer gelöscht", @@ -528,7 +528,7 @@ "migrations_no_such_migration": "Es existiert keine Migration genannt '{id}'", "migrations_running_forward": "Durchführen der Migrationen {id}...", "migrations_skip_migration": "Überspringe Migrationen {id}...", - "password_too_simple_2": "Dieses Passwort gehört zu den meistverwendeten der Welt. Bitte nehmen Sie etwas einzigartigeres", + "password_too_simple_2": "Das Passwort muss mindestens 8 Zeichen lang sein und Gross- sowie Kleinbuchstaben enthalten", "password_listed": "Dieses Passwort zählt zu den meistgenutzten Passwörtern der Welt. Bitte wähle ein anderes, einzigartigeres Passwort.", "operation_interrupted": "Wurde die Operation manuell unterbrochen?", "invalid_number": "Muss eine Zahl sein", @@ -539,8 +539,8 @@ "permission_already_allowed": "Die Gruppe '{group}' hat die Berechtigung '{permission}' bereits erhalten", "pattern_password_app": "Entschuldigen Sie bitte! Passwörter dürfen folgende Zeichen nicht enthalten: {forbidden_chars}", "pattern_email_forward": "Es muss sich um eine gültige E-Mail-Adresse handeln. Das Symbol '+' wird akzeptiert (zum Beispiel : maxmuster@beispiel.com oder maxmuster+yunohost@beispiel.com)", - "password_too_simple_4": "Dass Passwort muss mindestens 12 Zeichen lang sein und Zahlen, Klein- und Grossbuchstaben und Sonderzeichen enthalten", - "password_too_simple_3": "Das Passwort muss mindestens 8 Zeichen lang sein und Zahlen, Klein- und Grossbuchstaben und Sonderzeichen enthalten", + "password_too_simple_4": "Das Passwort muss mindestens 12 Zeichen lang sein und Grossbuchstaben, Kleinbuchstaben, Zahlen und Sonderzeichen enthalten", + "password_too_simple_3": "Das Passwort muss mindestens 8 Zeichen lang sein und Grossbuchstaben, Kleinbuchstaben, Zahlen und Sonderzeichen enthalten", "regenconf_file_manually_removed": "Die Konfigurationsdatei '{conf}' wurde manuell gelöscht und wird nicht erstellt", "regenconf_file_manually_modified": "Die Konfigurationsdatei '{conf}' wurde manuell bearbeitet und wird nicht aktualisiert", "regenconf_file_kept_back": "Die Konfigurationsdatei '{conf}' sollte von \"regen-conf\" (Kategorie {category}) gelöscht werden, wurde aber beibehalten.", @@ -631,5 +631,9 @@ "unknown_main_domain_path": "Unbekannte:r Domain oder Pfad für '{app}'. Du musst eine Domain und einen Pfad setzen, um die URL für Berechtigungen zu setzen.", "yunohost_postinstall_end_tip": "Post-install ist fertig! Um das Setup abzuschliessen, wird empfohlen:\n - einen ersten Benutzer über den Bereich 'Benutzer*in' im Adminbereich hinzuzufügen (oder mit 'yunohost user create ' in der Kommandezeile);\n - mögliche Fehler zu diagnostizieren über den Bereich 'Diagnose' im Adminbereich (oder mit 'yunohost diagnosis run' in der Kommandozeile;\n - Die Abschnitte 'Install YunoHost' und 'Geführte Tour' im Administratorenhandbuch zu lesen: https://yunohost.org/admindoc.", "user_already_exists": "Der Benutzer '{user}' ist bereits vorhanden", - "update_apt_cache_warning": "Beim Versuch den Cache für APT (Debians Paketmanager) zu aktualisieren, ist etwas schief gelaufen. Hier ist ein Dump der Zeilen aus sources.list, die Ihnen vielleicht dabei helfen, das Problem zu identifizieren:\n{sourceslist}" + "update_apt_cache_warning": "Beim Versuch den Cache für APT (Debians Paketmanager) zu aktualisieren, ist etwas schief gelaufen. Hier ist ein Dump der Zeilen aus sources.list, die Ihnen vielleicht dabei helfen, das Problem zu identifizieren:\n{sourceslist}", + "global_settings_setting_security_webadmin_allowlist": "IP-Adressen, die auf die Verwaltungsseite zugreifen dürfen. Kommasepariert.", + "global_settings_setting_security_webadmin_allowlist_enabled": "Erlaube nur bestimmten IP-Adressen den Zugriff auf die Verwaltungsseite.", + "disk_space_not_sufficient_update": "Es ist nicht genügend Speicherplatz frei, um diese Applikation zu aktuallisieren", + "disk_space_not_sufficient_install": "Es ist nicht genügend Speicherplatz frei, um diese Applikation zu installieren" } From 226ab63393d3bf021750aa5ccdea0afaf736c3b0 Mon Sep 17 00:00:00 2001 From: ericgaspar Date: Mon, 9 Aug 2021 12:31:06 +0200 Subject: [PATCH 024/114] Update my.cnf --- data/templates/mysql/my.cnf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/mysql/my.cnf b/data/templates/mysql/my.cnf index de13b467d..429596cf5 100644 --- a/data/templates/mysql/my.cnf +++ b/data/templates/mysql/my.cnf @@ -30,7 +30,7 @@ skip-external-locking key_buffer_size = 16K max_allowed_packet = 16M table_open_cache = 4 -sort_buffer_size = 64K +sort_buffer_size = 256K read_buffer_size = 256K read_rnd_buffer_size = 256K net_buffer_length = 2K From 3535b374aed13faf1e1ce72b1830fab12b09e920 Mon Sep 17 00:00:00 2001 From: tituspijean Date: Wed, 11 Aug 2021 16:19:51 +0000 Subject: [PATCH 025/114] [mdns] Always broadcast yunohost.local --- bin/yunomdns | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/yunomdns b/bin/yunomdns index 123935871..bc4e4c0e1 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -167,6 +167,8 @@ if __name__ == '__main__': if r == 'domains' or r == 'all': import glob config['domains'] = [ d.rsplit('/',1)[1][:-2] for d in glob.glob('/etc/nginx/conf.d/*.local.d') ] + if 'yunohost.local' not in config['domains']: + config['domains'].append('yunohost.local') print('Regenerated domains list: ' + str(config['domains'])) updated = True From cad5c39dae9b89c6f815250b6ed24351d7ae40fc Mon Sep 17 00:00:00 2001 From: tituspijean Date: Wed, 11 Aug 2021 16:21:32 +0000 Subject: [PATCH 026/114] [mdns] Remove logging and asyncio dependencies --- bin/yunomdns | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index bc4e4c0e1..4f698a0ac 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -3,7 +3,6 @@ """ WIP Pythonic declaration of mDNS .local domains for YunoHost -Based off https://github.com/jstasiak/python-zeroconf/blob/master/tests/test_asyncio.py """ import subprocess @@ -13,8 +12,6 @@ import sys import argparse import yaml -import asyncio -import logging import socket from time import sleep from typing import List, Dict @@ -98,7 +95,6 @@ def get_network_interfaces(): return devices if __name__ == '__main__': - logging.basicConfig(level=logging.DEBUG) ### @@ -110,7 +106,6 @@ if __name__ == '__main__': Configuration file: /etc/yunohost/mdns.yml Subdomains are not supported. ''') - parser.add_argument('--debug', action='store_true') parser.add_argument('--regen', nargs='?', const='as_stored', choices=['domains', 'interfaces', 'all', 'as_stored'], help=''' Regenerates selection into the configuration file then starts mDNS broadcasting. @@ -125,13 +120,6 @@ if __name__ == '__main__': able.add_argument('--disable', action='store_true') args = parser.parse_args() - if args.debug: - logging.getLogger('zeroconf').setLevel(logging.DEBUG) - logging.getLogger('asyncio').setLevel(logging.DEBUG) - else: - logging.getLogger('zeroconf').setLevel(logging.WARNING) - logging.getLogger('asyncio').setLevel(logging.WARNING) - ### # CONFIG ### From f53f36e332fb40c89a9467b9e558dab828da2df9 Mon Sep 17 00:00:00 2001 From: tituspijean Date: Wed, 11 Aug 2021 17:58:50 +0000 Subject: [PATCH 027/114] [mdns] Fix yunohost.local broadcast --- bin/yunomdns | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index 4f698a0ac..7280aebc8 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -155,8 +155,6 @@ if __name__ == '__main__': if r == 'domains' or r == 'all': import glob config['domains'] = [ d.rsplit('/',1)[1][:-2] for d in glob.glob('/etc/nginx/conf.d/*.local.d') ] - if 'yunohost.local' not in config['domains']: - config['domains'].append('yunohost.local') print('Regenerated domains list: ' + str(config['domains'])) updated = True @@ -177,6 +175,9 @@ if __name__ == '__main__': print('No interface listed for broadcast.') sys.exit(0) + if 'yunohost.local' not in config['domains']: + config['domains'].append('yunohost.local') + zcs = {} interfaces = get_network_interfaces() for interface in config['interfaces']: From 9bef3105f1c2992bdac88c678ee30918a80024ed Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Aug 2021 20:13:51 +0200 Subject: [PATCH 028/114] Add dependency to python3-zeroconf --- debian/control | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/control b/debian/control index ef5061fe7..cabff028b 100644 --- a/debian/control +++ b/debian/control @@ -13,7 +13,8 @@ Depends: ${python3:Depends}, ${misc:Depends} , moulinette (>= 4.2), ssowat (>= 4.0) , python3-psutil, python3-requests, python3-dnspython, python3-openssl , python3-miniupnpc, python3-dbus, python3-jinja2 - , python3-toml, python3-packaging, python3-publicsuffix + , python3-toml, python3-packaging, python3-publicsuffix, + , python3-zeroconf, , apt, apt-transport-https, apt-utils, dirmngr , php7.3-common, php7.3-fpm, php7.3-ldap, php7.3-intl , mariadb-server, php7.3-mysql From a2a50d0e453164510640508c02958f21bfd20b05 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Aug 2021 20:29:29 +0200 Subject: [PATCH 029/114] mdns: Remove argument parsing because we won't really need this ? :Z --- bin/yunomdns | 71 +++------------------------------------------------- 1 file changed, 4 insertions(+), 67 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index 7280aebc8..3e3eea72f 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -6,10 +6,8 @@ Pythonic declaration of mDNS .local domains for YunoHost """ import subprocess -import os import re import sys -import argparse import yaml import socket @@ -96,30 +94,6 @@ def get_network_interfaces(): if __name__ == '__main__': - - ### - # ARGUMENTS - ### - - parser = argparse.ArgumentParser(description=''' - mDNS broadcast for .local domains. - Configuration file: /etc/yunohost/mdns.yml - Subdomains are not supported. - ''') - parser.add_argument('--regen', nargs='?', const='as_stored', choices=['domains', 'interfaces', 'all', 'as_stored'], - help=''' - Regenerates selection into the configuration file then starts mDNS broadcasting. - ''') - parser.add_argument('--set-regen', choices=['domains', 'interfaces', 'all', 'none'], - help=''' - Set which part of the configuration to be regenerated. - Implies --regen as_stored, with newly stored parameter. - ''') - able = parser.add_mutually_exclusive_group() - able.add_argument('--enable', action='store_true', help='Enables mDNS broadcast, and regenerates the configuration') - able.add_argument('--disable', action='store_true') - args = parser.parse_args() - ### # CONFIG ### @@ -128,48 +102,11 @@ if __name__ == '__main__': config = yaml.load(f) or {} updated = False - if args.enable: - config['enabled'] = True - args.regen = 'as_stored' - updated = True + required_fields = ["interfaces", "domains"] + missing_fields = [field for field in required_fields if field not in config] - if args.disable: - config['enabled'] = False - updated = True - - if args.set_regen: - config['regen'] = args.set_regen - args.regen = 'as_stored' - updated = True - - if args.regen: - if args.regen == 'as_stored': - r = config['regen'] - else: - r = args.regen - if r == 'none': - print('Regeneration disabled.') - if r == 'interfaces' or r == 'all': - config['interfaces'] = [ i for i in get_network_interfaces() ] - print('Regenerated interfaces list: ' + str(config['interfaces'])) - if r == 'domains' or r == 'all': - import glob - config['domains'] = [ d.rsplit('/',1)[1][:-2] for d in glob.glob('/etc/nginx/conf.d/*.local.d') ] - print('Regenerated domains list: ' + str(config['domains'])) - updated = True - - if updated: - with open('/etc/yunohost/mdns.yml', 'w') as f: - yaml.safe_dump(config, f, default_flow_style=False) - print('Configuration file updated.') - - ### - # MAIN SCRIPT - ### - - if config['enabled'] is not True: - print('YunomDNS is disabled.') - sys.exit(0) + if missing_fields: + print("The fields %s are required" % ', '.join(missing_fields)) if config['interfaces'] is None: print('No interface listed for broadcast.') From 5bf687d619ae685b574c84822b0c59469d832394 Mon Sep 17 00:00:00 2001 From: ppr Date: Wed, 11 Aug 2021 16:22:47 +0000 Subject: [PATCH 030/114] Translated using Weblate (French) Currently translated at 99.6% (635 of 637 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 208492d20..dea492e60 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -633,5 +633,7 @@ "diagnosis_sshd_config_inconsistent_details": "Veuillez exécuter yunohost settings set security.ssh.port -v VOTRE_PORT_SSH pour définir le port SSH, et vérifiez yunohost tools regen-conf ssh --dry-run --with-diff et yunohost tools regen-conf ssh --force pour réinitialiser votre configuration aux recommandations YunoHost.", "diagnosis_sshd_config_inconsistent": "Il semble que le port SSH a été modifié manuellement dans /etc/ssh/sshd_config. Depuis YunoHost 4.2, un nouveau paramètre global 'security.ssh.port' est disponible pour éviter de modifier manuellement la configuration.", "diagnosis_sshd_config_insecure": "La configuration SSH semble avoir été modifiée manuellement et n'est pas sécurisée car elle ne contient aucune directive 'AllowGroups' ou 'AllowUsers' pour limiter l'accès aux utilisateurs autorisés.", - "backup_create_size_estimation": "L'archive contiendra environ {size} de données." -} \ No newline at end of file + "backup_create_size_estimation": "L'archive contiendra environ {size} de données.", + "global_settings_setting_security_webadmin_allowlist": "Adresses IP autorisées à accéder à la page web du portail d'administration (webadmin). Elles doivent être séparées par une virgule.", + "global_settings_setting_security_webadmin_allowlist_enabled": "Autorisez seulement certaines IP à accéder à la page web du portail d'administration (webadmin)." +} From 98d295f5850ec99ccb83e94c70f3e100bbf159cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M?= Date: Thu, 12 Aug 2021 05:49:08 +0000 Subject: [PATCH 031/114] Translated using Weblate (Galician) Currently translated at 56.2% (358 of 637 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/gl/ --- locales/gl.json | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/locales/gl.json b/locales/gl.json index 1caa2f3a6..6ef2c45c1 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -67,7 +67,7 @@ "app_remove_after_failed_install": "Eliminando a app debido ao fallo na instalación...", "app_requirements_unmeet": "Non se cumpren os requerimentos de {app}, o paquete {pkgname} ({version}) debe ser {spec}", "app_requirements_checking": "Comprobando os paquetes requeridos por {app}...", - "app_removed": "{app} eliminada", + "app_removed": "{app} desinstalada", "app_not_properly_removed": "{app} non se eliminou de xeito correcto", "app_not_installed": "Non se puido atopar {app} na lista de apps instaladas: {all_apps}", "app_not_correctly_installed": "{app} semella que non está instalada correctamente", @@ -345,5 +345,17 @@ "global_settings_setting_smtp_relay_password": "Contrasinal no repetidor SMTP", "global_settings_setting_smtp_relay_user": "Conta de usuaria no repetidor SMTP", "global_settings_setting_smtp_relay_port": "Porto do repetidor SMTP", - "global_settings_setting_smtp_relay_host": "Servidor repetidor SMTP para enviar emails no lugar da túa instancia yunohost. É útil se estás nunha destas situacións: o teu porto 25 está bloqueado polo teu provedor ISP u VPN, se tes unha IP residencial nunha lista DUHL, se non podes configurar DNS inversa ou se este servidor non ten conexión directa a internet e queres utilizar outro para enviar os emails." -} \ No newline at end of file + "global_settings_setting_smtp_relay_host": "Servidor repetidor SMTP para enviar emails no lugar da túa instancia yunohost. É útil se estás nunha destas situacións: o teu porto 25 está bloqueado polo teu provedor ISP u VPN, se tes unha IP residencial nunha lista DUHL, se non podes configurar DNS inversa ou se este servidor non ten conexión directa a internet e queres utilizar outro para enviar os emails.", + "group_updated": "Grupo '{group}' actualizado", + "group_unknown": "Grupo descoñecido '{group}'", + "group_deletion_failed": "Non se eliminou o grupo '{group}': {error}", + "group_deleted": "Grupo '{group}' eliminado", + "group_cannot_be_deleted": "O grupo {group} non se pode eliminar manualmente.", + "group_cannot_edit_primary_group": "O grupo '{group}' non se pode editar manualmente. É o grupo primario que contén só a unha usuaria concreta.", + "group_cannot_edit_visitors": "O grupo 'visitors' non se pode editar manualmente. É un grupo especial que representa a tódas visitantes anónimas", + "group_cannot_edit_all_users": "O grupo 'all_users' non se pode editar manualmente. É un grupo especial que contén tódalas usuarias rexistradas en YunoHost", + "global_settings_setting_security_webadmin_allowlist": "Enderezos IP con permiso para acceder á webadmin. Separados por vírgulas.", + "global_settings_setting_security_webadmin_allowlist_enabled": "Permitir que só algúns IPs accedan á webadmin.", + "disk_space_not_sufficient_update": "Non hai espazo suficiente no disco para actualizar esta aplicación", + "disk_space_not_sufficient_install": "Non queda espazo suficiente no disco para instalar esta aplicación" +} From fe2e014b5667c48f9f983e0c17f7b1e1db884699 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Aug 2021 15:26:39 +0200 Subject: [PATCH 032/114] mdns: Rework mdns's conf handling such that it's generated by the regen-conf. Also drop avahi-daemon because not needed anymore. --- data/hooks/conf_regen/01-yunohost | 5 +- data/hooks/conf_regen/37-avahi-daemon | 37 --------- data/hooks/conf_regen/37-mdns | 75 +++++++++++++++++++ data/templates/avahi-daemon/avahi-daemon.conf | 68 ----------------- .../mdns}/yunomdns.service | 0 data/templates/yunohost/mdns.yml | 4 - data/templates/yunohost/services.yml | 2 +- debian/control | 2 +- debian/install | 1 - debian/postinst | 4 - 10 files changed, 78 insertions(+), 120 deletions(-) delete mode 100755 data/hooks/conf_regen/37-avahi-daemon create mode 100755 data/hooks/conf_regen/37-mdns delete mode 100644 data/templates/avahi-daemon/avahi-daemon.conf rename data/{other => templates/mdns}/yunomdns.service (100%) delete mode 100644 data/templates/yunohost/mdns.yml diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index d160b9e66..3d65d34cd 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -3,7 +3,6 @@ set -e services_path="/etc/yunohost/services.yml" -mdns_path="/etc/yunohost/mdns.yml" do_init_regen() { if [[ $EUID -ne 0 ]]; then @@ -19,11 +18,9 @@ do_init_regen() { [[ -f /etc/yunohost/current_host ]] \ || echo "yunohost.org" > /etc/yunohost/current_host - # copy default services, mdns, and firewall + # copy default services and firewall [[ -f $services_path ]] \ || cp services.yml "$services_path" - [[ -f $mdns_path ]] \ - || cp mdns.yml "$mdns_path" [[ -f /etc/yunohost/firewall.yml ]] \ || cp firewall.yml /etc/yunohost/firewall.yml diff --git a/data/hooks/conf_regen/37-avahi-daemon b/data/hooks/conf_regen/37-avahi-daemon deleted file mode 100755 index 4127d66ca..000000000 --- a/data/hooks/conf_regen/37-avahi-daemon +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -set -e - -do_pre_regen() { - pending_dir=$1 - - cd /usr/share/yunohost/templates/avahi-daemon - - install -D -m 644 avahi-daemon.conf \ - "${pending_dir}/etc/avahi/avahi-daemon.conf" -} - -do_post_regen() { - regen_conf_files=$1 - - [[ -z "$regen_conf_files" ]] \ - || systemctl restart avahi-daemon -} - -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 diff --git a/data/hooks/conf_regen/37-mdns b/data/hooks/conf_regen/37-mdns new file mode 100755 index 000000000..903b41a0f --- /dev/null +++ b/data/hooks/conf_regen/37-mdns @@ -0,0 +1,75 @@ +#!/bin/bash + +set -e + +_generate_config() { + echo "domains:" + echo " - yunohost.local" + for domain in $YNH_DOMAINS + do + # Only keep .local domains (don't keep + [[ "$domain" =~ [^.]+\.[^.]+\.local$ ]] && echo "Subdomain $domain cannot be handled by Bonjour/Zeroconf/mDNS" >&2 + [[ "$domain" =~ ^[^.]+\.local$ ]] || continue + echo " - $domain" + done + + echo "interfaces:" + local_network_interfaces="$(ip --brief a | grep ' 10\.\| 192\.168\.' | awk '{print $1}')" + for interface in $local_network_interfaces + do + echo " - $interface" + done +} + +do_init_regen() { + do_pre_regen + do_post_regen /etc/systemd/system/yunomdns.service + systemctl enable yunomdns +} + +do_pre_regen() { + pending_dir="$1" + + cd /usr/share/yunohost/templates/dnsmasq + cp yunomdns.service ${pending_dir}/etc/systemd/system/ + + getent passwd mdns &>/dev/null || useradd --no-create-home --shell /usr/sbin/nologin --system --user-group mdns + + _generate_config > ${pending_dir}/etc/yunohost/mdns.yml +} + +do_post_regen() { + regen_conf_files="$1" + + chown mdns:mdns ${pending_dir}/etc/yunohost/mdns.yml + + # If we changed the systemd ynh-override conf + if echo "$regen_conf_files" | sed 's/,/\n/g' | grep -q "^/etc/systemd/system/yunomdns.service$" + then + systemctl daemon-reload + fi + + [[ -z "$regen_conf_files" ]] \ + || systemctl restart yunomdns +} + +FORCE=${2:-0} +DRY_RUN=${3:-0} + +case "$1" in + pre) + do_pre_regen $4 + ;; + post) + do_post_regen $4 + ;; + init) + do_init_regen + ;; + *) + echo "hook called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/templates/avahi-daemon/avahi-daemon.conf b/data/templates/avahi-daemon/avahi-daemon.conf deleted file mode 100644 index d3542a411..000000000 --- a/data/templates/avahi-daemon/avahi-daemon.conf +++ /dev/null @@ -1,68 +0,0 @@ -# This file is part of avahi. -# -# avahi is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# avahi is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public -# License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with avahi; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA. - -# See avahi-daemon.conf(5) for more information on this configuration -# file! - -[server] -host-name=yunohost -domain-name=local -#browse-domains=0pointer.de, zeroconf.org -use-ipv4=yes -use-ipv6=yes -#allow-interfaces=eth0 -#deny-interfaces=eth1 -#check-response-ttl=no -#use-iff-running=no -#enable-dbus=yes -#disallow-other-stacks=no -#allow-point-to-point=no -#cache-entries-max=4096 -#clients-max=4096 -#objects-per-client-max=1024 -#entries-per-entry-group-max=32 -ratelimit-interval-usec=1000000 -ratelimit-burst=1000 - -[wide-area] -enable-wide-area=yes - -[publish] -#disable-publishing=no -#disable-user-service-publishing=no -#add-service-cookie=no -#publish-addresses=yes -#publish-hinfo=yes -#publish-workstation=yes -#publish-domain=yes -#publish-dns-servers=192.168.50.1, 192.168.50.2 -#publish-resolv-conf-dns-servers=yes -#publish-aaaa-on-ipv4=yes -#publish-a-on-ipv6=no - -[reflector] -#enable-reflector=no -#reflect-ipv=no - -[rlimits] -#rlimit-as= -rlimit-core=0 -rlimit-data=4194304 -rlimit-fsize=0 -rlimit-nofile=768 -rlimit-stack=4194304 -rlimit-nproc=3 diff --git a/data/other/yunomdns.service b/data/templates/mdns/yunomdns.service similarity index 100% rename from data/other/yunomdns.service rename to data/templates/mdns/yunomdns.service diff --git a/data/templates/yunohost/mdns.yml b/data/templates/yunohost/mdns.yml deleted file mode 100644 index 3ed9e792b..000000000 --- a/data/templates/yunohost/mdns.yml +++ /dev/null @@ -1,4 +0,0 @@ -enabled: True -regen: all -interfaces: -domains: diff --git a/data/templates/yunohost/services.yml b/data/templates/yunohost/services.yml index 447829684..c7690fc9c 100644 --- a/data/templates/yunohost/services.yml +++ b/data/templates/yunohost/services.yml @@ -1,4 +1,3 @@ -avahi-daemon: {} dnsmasq: test_conf: dnsmasq --test dovecot: @@ -71,3 +70,4 @@ rmilter: null php5-fpm: null php7.0-fpm: null nslcd: null +avahi-daemon: null diff --git a/debian/control b/debian/control index cabff028b..c9306bef1 100644 --- a/debian/control +++ b/debian/control @@ -21,7 +21,7 @@ Depends: ${python3:Depends}, ${misc:Depends} , openssh-server, iptables, fail2ban, dnsutils, bind9utils , openssl, ca-certificates, netcat-openbsd, iproute2 , slapd, ldap-utils, sudo-ldap, libnss-ldapd, unscd, libpam-ldapd - , dnsmasq, avahi-daemon, libnss-mdns, resolvconf, libnss-myhostname + , dnsmasq, libnss-mdns, resolvconf, libnss-myhostname , postfix, postfix-ldap, postfix-policyd-spf-perl, postfix-pcre , dovecot-core, dovecot-ldap, dovecot-lmtpd, dovecot-managesieved, dovecot-antispam , rspamd, opendkim-tools, postsrsd, procmail, mailutils diff --git a/debian/install b/debian/install index e30a69a8b..1691a4849 100644 --- a/debian/install +++ b/debian/install @@ -5,7 +5,6 @@ doc/yunohost.8.gz /usr/share/man/man8/ data/actionsmap/* /usr/share/moulinette/actionsmap/ data/hooks/* /usr/share/yunohost/hooks/ data/other/yunoprompt.service /etc/systemd/system/ -data/other/yunomdns.service /etc/systemd/system/ data/other/password/* /usr/share/yunohost/other/password/ data/other/dpkg-origins/yunohost /etc/dpkg/origins data/other/dnsbl_list.yml /usr/share/yunohost/other/ diff --git a/debian/postinst b/debian/postinst index 7590197bd..ecae9b258 100644 --- a/debian/postinst +++ b/debian/postinst @@ -38,10 +38,6 @@ do_configure() { # Yunoprompt systemctl enable yunoprompt.service - - # Yunomdns - chown avahi:avahi /etc/yunohost/mdns.yml - systemctl enable yunomdns.service } # summary of how this script can be called: From 8dd65f78c8cc2e3631c2836b7596a238a8b654b4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Aug 2021 15:27:19 +0200 Subject: [PATCH 033/114] mdns: Propagate new service name to i18n strings --- locales/ar.json | 2 +- locales/ca.json | 2 +- locales/de.json | 2 +- locales/en.json | 2 +- locales/eo.json | 2 +- locales/es.json | 2 +- locales/fr.json | 2 +- locales/it.json | 2 +- locales/oc.json | 2 +- locales/zh_Hans.json | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/locales/ar.json b/locales/ar.json index 06e444f4a..6a057bdc8 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -83,7 +83,7 @@ "yunohost_installing": "عملية تنصيب يونوهوست جارية …", "yunohost_not_installed": "إنَّ واي يونوهوست ليس مُنَصَّب أو هو مثبت حاليا بشكل خاطئ. قم بتنفيذ الأمر 'yunohost tools postinstall'", "migrations_list_conflict_pending_done": "لا يمكنك استخدام --previous و --done معًا على نفس سطر الأوامر.", - "service_description_avahi-daemon": "يسمح لك بالنفاذ إلى خادومك عبر الشبكة المحلية باستخدام yunohost.local", + "service_description_mdns": "يسمح لك بالنفاذ إلى خادومك عبر الشبكة المحلية باستخدام yunohost.local", "service_description_metronome": "يُدير حسابات الدردشة الفورية XMPP", "service_description_nginx": "يقوم بتوفير النفاذ و السماح بالوصول إلى كافة مواقع الويب المستضافة على خادومك", "service_description_postfix": "يقوم بإرسال و تلقي الرسائل البريدية الإلكترونية", diff --git a/locales/ca.json b/locales/ca.json index 1e4c55f5d..c9f71c0ad 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -283,7 +283,7 @@ "service_already_started": "El servei «{service:s}» ja està funcionant", "service_already_stopped": "Ja s'ha aturat el servei «{service:s}»", "service_cmd_exec_failed": "No s'ha pogut executar l'ordre «{command:s}»", - "service_description_avahi-daemon": "Permet accedir al servidor via «yunohost.local» en la xarxa local", + "service_description_mdns": "Permet accedir al servidor via «yunohost.local» en la xarxa local", "service_description_dnsmasq": "Gestiona la resolució del nom de domini (DNS)", "service_description_dovecot": "Permet als clients de correu accedir/recuperar correus (via IMAP i POP3)", "service_description_fail2ban": "Protegeix contra els atacs de força bruta i a altres atacs provinents d'Internet", diff --git a/locales/de.json b/locales/de.json index 83647ec17..0760dc775 100644 --- a/locales/de.json +++ b/locales/de.json @@ -597,7 +597,7 @@ "service_description_fail2ban": "Schützt gegen Brute-Force-Angriffe und andere Angriffe aus dem Internet", "service_description_dovecot": "Ermöglicht es E-Mail-Clients auf Konten zuzugreifen (IMAP und POP3)", "service_description_dnsmasq": "Verarbeitet die Auflösung des Domainnamens (DNS)", - "service_description_avahi-daemon": "Erlaubt, den Server im lokalen Netz über 'yunohost.local' zu erreichen", + "service_description_mdns": "Erlaubt, den Server im lokalen Netz über 'yunohost.local' zu erreichen", "restore_backup_too_old": "Dieses Backup kann nicht wieder hergestellt werden, weil es von einer zu alten YunoHost Version stammt.", "service_description_slapd": "Speichert Benutzer, Domains und verbundene Informationen", "service_description_rspamd": "Spamfilter und andere E-Mail-Merkmale", diff --git a/locales/en.json b/locales/en.json index 3734b7cf3..5c73cba8b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -558,7 +558,7 @@ "service_already_started": "The service '{service:s}' is running already", "service_already_stopped": "The service '{service:s}' has already been stopped", "service_cmd_exec_failed": "Could not execute the command '{command:s}'", - "service_description_avahi-daemon": "Allows you to reach your server using 'yunohost.local' in your local network", + "service_description_mdns": "Allows you to reach your server using 'yunohost.local' in your local network", "service_description_dnsmasq": "Handles domain name resolution (DNS)", "service_description_dovecot": "Allows e-mail clients to access/fetch email (via IMAP and POP3)", "service_description_fail2ban": "Protects against brute-force and other kinds of attacks from the Internet", diff --git a/locales/eo.json b/locales/eo.json index d273593a9..b4d53f0f1 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -332,7 +332,7 @@ "hook_exec_failed": "Ne povis funkcii skripto: {path:s}", "global_settings_cant_open_settings": "Ne eblis malfermi agordojn, tial: {reason:s}", "user_created": "Uzanto kreita", - "service_description_avahi-daemon": "Permesas al vi atingi vian servilon uzante 'yunohost.local' en via loka reto", + "service_description_mdns": "Permesas al vi atingi vian servilon uzante 'yunohost.local' en via loka reto", "certmanager_attempt_to_replace_valid_cert": "Vi provas anstataŭigi bonan kaj validan atestilon por domajno {domain:s}! (Uzu --forte pretervidi)", "regenconf_updated": "Agordo ĝisdatigita por '{category}'", "update_apt_cache_warning": "Io iris malbone dum la ĝisdatigo de la kaŝmemoro de APT (paka administranto de Debian). Jen rubujo de la sources.list-linioj, kiuj povus helpi identigi problemajn liniojn:\n{sourceslist}", diff --git a/locales/es.json b/locales/es.json index f95451922..f2b8063cf 100644 --- a/locales/es.json +++ b/locales/es.json @@ -238,7 +238,7 @@ "service_description_fail2ban": "Protege contra ataques de fuerza bruta y otras clases de ataques desde Internet", "service_description_dovecot": "Permite a los clientes de correo acceder/obtener correo (vía IMAP y POP3)", "service_description_dnsmasq": "Maneja la resolución de nombres de dominio (DNS)", - "service_description_avahi-daemon": "Permite acceder a su servidor usando «yunohost.local» en su red local", + "service_description_mdns": "Permite acceder a su servidor usando «yunohost.local» en su red local", "server_reboot_confirm": "El servidor se reiniciará inmediatamente ¿está seguro? [{answers:s}]", "server_reboot": "El servidor se reiniciará", "server_shutdown_confirm": "El servidor se apagará inmediatamente ¿está seguro? [{answers:s}]", diff --git a/locales/fr.json b/locales/fr.json index f06acf2e5..565cc8032 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -232,7 +232,7 @@ "migrations_list_conflict_pending_done": "Vous ne pouvez pas utiliser --previous et --done simultanément.", "migrations_to_be_ran_manually": "La migration {id} doit être lancée manuellement. Veuillez aller dans Outils > Migrations dans l’interface admin, ou lancer `yunohost tools migrations run`.", "migrations_need_to_accept_disclaimer": "Pour lancer la migration {id}, vous devez accepter cet avertissement :\n---\n{disclaimer}\n---\nSi vous acceptez de lancer la migration, veuillez relancer la commande avec l’option --accept-disclaimer.", - "service_description_avahi-daemon": "Vous permet d’atteindre votre serveur en utilisant « yunohost.local » sur votre réseau local", + "service_description_mdns": "Vous permet d’atteindre votre serveur en utilisant « yunohost.local » sur votre réseau local", "service_description_dnsmasq": "Gère la résolution des noms de domaine (DNS)", "service_description_dovecot": "Permet aux clients de messagerie d’accéder/récupérer les courriels (via IMAP et POP3)", "service_description_fail2ban": "Protège contre les attaques brute-force et autres types d’attaques venant d’Internet", diff --git a/locales/it.json b/locales/it.json index 707f3afc2..3444b846d 100644 --- a/locales/it.json +++ b/locales/it.json @@ -428,7 +428,7 @@ "service_description_fail2ban": "Ti protegge dal brute-force e altri tipi di attacchi da Internet", "service_description_dovecot": "Consente ai client mail di accedere/recuperare le email (via IMAP e POP3)", "service_description_dnsmasq": "Gestisce la risoluzione dei domini (DNS)", - "service_description_avahi-daemon": "Consente di raggiungere il tuo server eseguendo 'yunohost.local' sulla tua LAN", + "service_description_mdns": "Consente di raggiungere il tuo server eseguendo 'yunohost.local' sulla tua LAN", "server_reboot_confirm": "Il server si riavvierà immediatamente, sei sicuro? [{answers:s}]", "server_reboot": "Il server si riavvierà", "server_shutdown_confirm": "Il server si spegnerà immediatamente, sei sicuro? [{answers:s}]", diff --git a/locales/oc.json b/locales/oc.json index 991383bc3..b5bd9474c 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -193,7 +193,7 @@ "user_unknown": "Utilizaire « {user:s} » desconegut", "user_update_failed": "Modificacion impossibla de l’utilizaire", "user_updated": "L’utilizaire es estat modificat", - "service_description_avahi-daemon": "permet d’aténher vòstre servidor via yunohost.local sus vòstre ret local", + "service_description_mdns": "permet d’aténher vòstre servidor via yunohost.local sus vòstre ret local", "service_description_dnsmasq": "gerís la resolucion dels noms de domeni (DNS)", "updating_apt_cache": "Actualizacion de la lista dels paquets disponibles…", "server_reboot_confirm": "Lo servidor es per reaviar sul pic, o volètz vertadièrament ? {answers:s}", diff --git a/locales/zh_Hans.json b/locales/zh_Hans.json index 78ba55133..0e971299f 100644 --- a/locales/zh_Hans.json +++ b/locales/zh_Hans.json @@ -226,7 +226,7 @@ "service_description_fail2ban": "防止来自互联网的暴力攻击和其他类型的攻击", "service_description_dovecot": "允许电子邮件客户端访问/获取电子邮件(通过IMAP和POP3)", "service_description_dnsmasq": "处理域名解析(DNS)", - "service_description_avahi-daemon": "允许您使用本地网络中的“ yunohost.local”访问服务器", + "service_description_mdns": "允许您使用本地网络中的“ yunohost.local”访问服务器", "service_started": "服务 '{service:s}' 已启动", "service_start_failed": "无法启动服务 '{service:s}'\n\n最近的服务日志:{logs:s}", "service_reloaded_or_restarted": "服务'{service:s}'已重新加载或重新启动", From ffc132f2c5192e393275250bb1073bd2ee499c7f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Aug 2021 16:59:54 +0200 Subject: [PATCH 034/114] Configure mdns during yunohost's debian postinst init --- debian/postinst | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/postinst b/debian/postinst index ecae9b258..8fc00bbe2 100644 --- a/debian/postinst +++ b/debian/postinst @@ -18,6 +18,7 @@ do_configure() { bash /usr/share/yunohost/hooks/conf_regen/46-nsswitch init bash /usr/share/yunohost/hooks/conf_regen/06-slapd init bash /usr/share/yunohost/hooks/conf_regen/15-nginx init + bash /usr/share/yunohost/hooks/conf_regen/37-mdns init fi else echo "Regenerating configuration, this might take a while..." From 4d0581bef21865903202a8cd911fd7dec0db7285 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Aug 2021 17:19:45 +0200 Subject: [PATCH 035/114] mdns: Misc fixes after tests on the battefield --- data/hooks/conf_regen/37-mdns | 6 ++++-- src/yunohost/domain.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/data/hooks/conf_regen/37-mdns b/data/hooks/conf_regen/37-mdns index 903b41a0f..c85e43a9a 100755 --- a/data/hooks/conf_regen/37-mdns +++ b/data/hooks/conf_regen/37-mdns @@ -30,18 +30,20 @@ do_init_regen() { do_pre_regen() { pending_dir="$1" - cd /usr/share/yunohost/templates/dnsmasq + cd /usr/share/yunohost/templates/mdns + mkdir -p ${pending_dir}/etc/systemd/system/ cp yunomdns.service ${pending_dir}/etc/systemd/system/ getent passwd mdns &>/dev/null || useradd --no-create-home --shell /usr/sbin/nologin --system --user-group mdns + mkdir -p ${pending_dir}/etc/yunohost _generate_config > ${pending_dir}/etc/yunohost/mdns.yml } do_post_regen() { regen_conf_files="$1" - chown mdns:mdns ${pending_dir}/etc/yunohost/mdns.yml + chown mdns:mdns /etc/yunohost/mdns.yml # If we changed the systemd ynh-override conf if echo "$regen_conf_files" | sed 's/,/\n/g' | grep -q "^/etc/systemd/system/yunomdns.service$" diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index aaac3a995..bca701dc6 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -163,7 +163,7 @@ def domain_add(operation_logger, domain, dyndns=False): # because it's one of the major service, but in the long term we # should identify the root of this bug... _force_clear_hashes(["/etc/nginx/conf.d/%s.conf" % domain]) - regen_conf(names=["nginx", "metronome", "dnsmasq", "postfix", "rspamd"]) + regen_conf(names=["nginx", "metronome", "dnsmasq", "postfix", "rspamd", "mdns"]) app_ssowatconf() except Exception as e: @@ -290,7 +290,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False): "/etc/nginx/conf.d/%s.conf" % domain, new_conf=None, save=True ) - regen_conf(names=["nginx", "metronome", "dnsmasq", "postfix"]) + regen_conf(names=["nginx", "metronome", "dnsmasq", "postfix", "rspamd", "mdns"]) app_ssowatconf() hook_callback("post_domain_remove", args=[domain]) From a343490f304d9a3a0a8cc1e64e73a7eb83064fbe Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Aug 2021 17:22:20 +0200 Subject: [PATCH 036/114] We shall not expose port 5353 publicly --- data/templates/yunohost/services.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/data/templates/yunohost/services.yml b/data/templates/yunohost/services.yml index c7690fc9c..c781d54aa 100644 --- a/data/templates/yunohost/services.yml +++ b/data/templates/yunohost/services.yml @@ -52,7 +52,6 @@ yunohost-firewall: test_status: iptables -S | grep "^-A INPUT" | grep " --dport" | grep -q ACCEPT category: security yunomdns: - needs_exposed_ports: [5353] category: mdns glances: null nsswitch: null From 212ea635df64ca8a2d2a9167529c52204d89c5ea Mon Sep 17 00:00:00 2001 From: tituspijean Date: Thu, 12 Aug 2021 15:52:25 +0000 Subject: [PATCH 037/114] [mdns] Fix service user --- data/templates/mdns/yunomdns.service | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/templates/mdns/yunomdns.service b/data/templates/mdns/yunomdns.service index 36d938035..ce2641b5d 100644 --- a/data/templates/mdns/yunomdns.service +++ b/data/templates/mdns/yunomdns.service @@ -3,8 +3,8 @@ Description=YunoHost mDNS service After=network.target [Service] -User=avahi -Group=avahi +User=mdns +Group=mdns Type=simple ExecStart=/usr/bin/yunomdns StandardOutput=syslog From f1444bc36ffea201890e6e676ef71459e20c30ba Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Aug 2021 18:40:43 +0200 Subject: [PATCH 038/114] mdns: misc fixes for ip parsing --- bin/yunomdns | 53 ++++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index 3e3eea72f..be5a87be0 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -47,7 +47,7 @@ def _extract_inet(string, skip_netmask=False, skip_loopback=True): ip4_pattern = ( r"((25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}" ) - ip6_pattern = r"(((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)" + ip6_pattern = r"(((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::?((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)" ip4_pattern += r"/[0-9]{1,2})" if not skip_netmask else ")" ip6_pattern += r"/[0-9]{1,3})" if not skip_netmask else ")" result = {} @@ -74,14 +74,16 @@ def _extract_inet(string, skip_netmask=False, skip_loopback=True): # Helper command taken from Moulinette def get_network_interfaces(): + # Get network devices and their addresses (raw infos from 'ip addr') devices_raw = {} - output = check_output("ip addr show") - for d in re.split(r"^(?:[0-9]+: )", output, flags=re.MULTILINE): - # Extract device name (1) and its addresses (2) - m = re.match(r"([^\s@]+)(?:@[\S]+)?: (.*)", d, flags=re.DOTALL) - if m: - devices_raw[m.group(1)] = m.group(2) + output = check_output("ip --brief a").split("\n") + for line in output: + line = line.split() + iname = line[0] + ips = ' '.join(line[2:]) + + devices_raw[iname] = ips # Parse relevant informations for each of them devices = { @@ -122,25 +124,18 @@ if __name__ == '__main__': ips = [] # Human-readable IPs b_ips = [] # Binary-convered IPs - # Parse the IPs and prepare their binary version - addressed = False - try: - ip = interfaces[interface]['ipv4'].split('/')[0] - if len(ip)>0: addressed = True - ips.append(ip) - b_ips.append(socket.inet_pton(socket.AF_INET, ip)) - except: - pass - try: - ip = interfaces[interface]['ipv6'].split('/')[0] - if len(ip)>0: addressed = True - ips.append(ip) - b_ips.append(socket.inet_pton(socket.AF_INET6, ip)) - except: - pass + ipv4 = interfaces[interface]['ipv4'].split('/')[0] + if ipv4: + ips.append(ipv4) + b_ips.append(socket.inet_pton(socket.AF_INET, ipv4)) + + ipv6 = interfaces[interface]['ipv6'].split('/')[0] + if ipv6: + ips.append(ipv6) + b_ips.append(socket.inet_pton(socket.AF_INET6, ipv6)) # If at least one IP is listed - if addressed: + if ips: # Create a Zeroconf object, and store the ServiceInfos zc = Zeroconf(interfaces=ips) zcs[zc]=[] @@ -151,11 +146,11 @@ if __name__ == '__main__': else: # Create a ServiceInfo object for each .local domain zcs[zc].append(ServiceInfo( - type_='_device-info._tcp.local.', - name=interface+': '+d_domain+'._device-info._tcp.local.', - addresses=b_ips, - port=80, - server=d+'.', + type_='_device-info._tcp.local.', + name=interface+': '+d_domain+'._device-info._tcp.local.', + addresses=b_ips, + port=80, + server=d+'.', )) print('Adding '+d+' with addresses '+str(ips)+' on interface '+interface) From 7ca637d8f82f0a888d3d8af93950e0692b125821 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Aug 2021 18:54:20 +0200 Subject: [PATCH 039/114] yaml.load -> yaml.safe_load --- bin/yunomdns | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/yunomdns b/bin/yunomdns index be5a87be0..862a1f477 100755 --- a/bin/yunomdns +++ b/bin/yunomdns @@ -1,7 +1,6 @@ #!/usr/bin/env python3 """ -WIP Pythonic declaration of mDNS .local domains for YunoHost """ @@ -101,7 +100,7 @@ if __name__ == '__main__': ### with open('/etc/yunohost/mdns.yml', 'r') as f: - config = yaml.load(f) or {} + config = yaml.safe_load(f) or {} updated = False required_fields = ["interfaces", "domains"] From 849ccb043a94bbc60c608cf12665e9453df03455 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Aug 2021 19:13:26 +0200 Subject: [PATCH 040/114] mdns: diagnosis expects a 'data' key instead of 'results' --- data/hooks/diagnosis/12-dnsrecords.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index 89816847d..49e39c775 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -65,14 +65,14 @@ class DNSRecordsDiagnoser(Diagnoser): if is_subdomain: yield dict( meta={"domain": domain, "category": "basic"}, - results={}, + data={}, status="WARNING", summary="diagnosis_domain_subdomain_localdomain", ) else: yield dict( meta={"domain": domain, "category": "basic"}, - results={}, + data={}, status="INFO", summary="diagnosis_domain_localdomain", ) From 7f3eeafbed74af010c31e46f8753c2a4bd165fdd Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Aug 2021 19:40:36 +0200 Subject: [PATCH 041/114] mdns/diagnosis: following suggestion IRL: extend the no-check mecanism for .local to .onion and other special-use TLDs --- data/hooks/diagnosis/12-dnsrecords.py | 30 ++++++++++----------------- locales/en.json | 3 +-- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index 49e39c775..c29029de9 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -13,7 +13,7 @@ from yunohost.diagnosis import Diagnoser from yunohost.domain import domain_list, _build_dns_conf, _get_maindomain YNH_DYNDNS_DOMAINS = ["nohost.me", "noho.st", "ynh.fr"] - +SPECIAL_USE_TLDS = ["local", "localhost", "onion", "dev", "test"] class DNSRecordsDiagnoser(Diagnoser): @@ -29,9 +29,9 @@ class DNSRecordsDiagnoser(Diagnoser): for domain in all_domains: self.logger_debug("Diagnosing DNS conf for %s" % domain) is_subdomain = domain.split(".", 1)[1] in all_domains - is_localdomain = domain.endswith(".local") + is_specialusedomain = any(domain.endswith("." + tld) for tld in SPECIAL_USE_TLDS) for report in self.check_domain( - domain, domain == main_domain, is_subdomain=is_subdomain, is_localdomain=is_localdomain + domain, domain == main_domain, is_subdomain=is_subdomain, is_specialusedomain=is_specialusedomain ): yield report @@ -49,7 +49,7 @@ class DNSRecordsDiagnoser(Diagnoser): for report in self.check_expiration_date(domains_from_registrar): yield report - def check_domain(self, domain, is_main_domain, is_subdomain, is_localdomain): + def check_domain(self, domain, is_main_domain, is_subdomain, is_specialusedomain): expected_configuration = _build_dns_conf( domain, include_empty_AAAA_if_no_ipv6=True @@ -60,22 +60,14 @@ class DNSRecordsDiagnoser(Diagnoser): if is_subdomain: categories = ["basic"] - if is_localdomain: + if is_specialusedomain: categories = [] - if is_subdomain: - yield dict( - meta={"domain": domain, "category": "basic"}, - data={}, - status="WARNING", - summary="diagnosis_domain_subdomain_localdomain", - ) - else: - yield dict( - meta={"domain": domain, "category": "basic"}, - data={}, - status="INFO", - summary="diagnosis_domain_localdomain", - ) + yield dict( + meta={"domain": domain}, + data={}, + status="INFO", + summary="diagnosis_dns_specialusedomain", + ) for category in categories: diff --git a/locales/en.json b/locales/en.json index 2b8e8b32f..cad1c8dcb 100644 --- a/locales/en.json +++ b/locales/en.json @@ -183,6 +183,7 @@ "diagnosis_dns_discrepancy": "The following DNS record does not seem to follow the recommended configuration:
Type: {type}
Name: {name}
Current value: {current}
Expected value: {value}", "diagnosis_dns_point_to_doc": "Please check the documentation at https://yunohost.org/dns_config 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 yunohost dyndns update --force.", + "diagnosis_dns_specialusedomain": "Domain {domain} is based on a special-use top-level domain (TLD) and is therefore not expected to have actual DNS records.", "diagnosis_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_expiration_not_found_details": "The WHOIS information for domain {domain} doesn't seem to contain the information about the expiration date?", @@ -190,8 +191,6 @@ "diagnosis_domain_expiration_warning": "Some domains will expire soon!", "diagnosis_domain_expiration_error": "Some domains will expire VERY SOON!", "diagnosis_domain_expires_in": "{domain} expires in {days} days.", - "diagnosis_domain_localdomain": "Domain {domain}, with a .local TLD, is not expected to have DNS records as it can be discovered through mDNS.", - "diagnosis_domain_subdomain_localdomain": "Domain {domain} is a subdomain of a .local domain. Zeroconf/mDNS discovery only works with first-level domains.", "diagnosis_services_running": "Service {service} is running!", "diagnosis_services_conf_broken": "Configuration is broken for service {service}!", "diagnosis_services_bad_status": "Service {service} is {status} :(", From 66b036512bcf1912e32915a64f405d7f0d229341 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Aug 2021 19:50:16 +0200 Subject: [PATCH 042/114] README: Update shields --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aec5300e2..fa3c839c9 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@
-[![Build status](https://travis-ci.org/YunoHost/yunohost.svg?branch=stretch-unstable)](https://travis-ci.org/YunoHost/yunohost) -[![GitHub license](https://img.shields.io/github/license/YunoHost/yunohost)](https://github.com/YunoHost/yunohost/blob/stretch-unstable/LICENSE) +[![Build status](https://shields.io/gitlab/pipeline/yunohost/yunohost/dev)](https://gitlab.com/yunohost/yunohost/-/pipelines) +[![GitHub license](https://img.shields.io/github/license/YunoHost/yunohost)](https://github.com/YunoHost/yunohost/blob/dev/LICENSE) [![Mastodon Follow](https://img.shields.io/mastodon/follow/28084)](https://mastodon.social/@yunohost)
From e493b89d6a5eb2301e6663277aa03ac404329e87 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Aug 2021 20:16:35 +0200 Subject: [PATCH 043/114] diagnosis: Zblerg, .dev is actually an actual TLD --- data/hooks/diagnosis/12-dnsrecords.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index c29029de9..1db4af685 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -13,7 +13,8 @@ from yunohost.diagnosis import Diagnoser from yunohost.domain import domain_list, _build_dns_conf, _get_maindomain YNH_DYNDNS_DOMAINS = ["nohost.me", "noho.st", "ynh.fr"] -SPECIAL_USE_TLDS = ["local", "localhost", "onion", "dev", "test"] +SPECIAL_USE_TLDS = ["local", "localhost", "onion", "test"] + class DNSRecordsDiagnoser(Diagnoser): From f0590907c9e2eebe46d28146698387a33a8aea76 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Sat, 14 Aug 2021 11:44:52 +0200 Subject: [PATCH 044/114] fix ynh_permission_has_user --- data/helpers.d/permission | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/permission b/data/helpers.d/permission index a5c09cded..c04b4145b 100644 --- a/data/helpers.d/permission +++ b/data/helpers.d/permission @@ -362,8 +362,17 @@ ynh_permission_has_user() { return 1 fi - yunohost user permission info "$app.$permission" --output-as json --quiet \ - | jq -e --arg user $user '.corresponding_users | index($user)' >/dev/null + # Check both allowed and corresponding_users sections in the json + for section in "allowed" "corresponding_users" + do + if yunohost user permission info "$app.$permission" --output-as json --quiet \ + | jq -e --arg user $user --arg section $section '.[$section] | index($user)' >/dev/null + then + return 0 + fi + done + + return 1 } # Check if a legacy permissions exist From c2460d7526069ed48ff2b0bd5a35c8c3f2086c76 Mon Sep 17 00:00:00 2001 From: sagessylu Date: Sun, 15 Aug 2021 21:41:32 +0200 Subject: [PATCH 045/114] Add purge option to yunohost app remove --- data/actionsmap/yunohost.yml | 6 +++++- src/yunohost/app.py | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 5df1c0877..bd9207528 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -673,7 +673,11 @@ app: api: DELETE /apps/ arguments: app: - help: App to delete + help: App to remove + -p: + full: --purge + help: Remove with all app data + action: store_true ### app_upgrade() upgrade: diff --git a/src/yunohost/app.py b/src/yunohost/app.py index a48400a8e..d6b459264 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1187,12 +1187,13 @@ def dump_app_log_extract_for_debugging(operation_logger): @is_unit_operation() -def app_remove(operation_logger, app): +def app_remove(operation_logger, app, purge=False): """ Remove app - Keyword argument: + Keyword arguments: app -- App(s) to delete + purge -- Remove with all app data """ from yunohost.hook import hook_exec, hook_remove, hook_callback @@ -1230,6 +1231,7 @@ def app_remove(operation_logger, app): env_dict["YNH_APP_INSTANCE_NAME"] = app env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?") + env_dict["YNH_APP_PURGE"] = purge operation_logger.extra.update({"env": env_dict}) operation_logger.flush() From cec4971cac0aa567149349e2c4f27efbd2f45387 Mon Sep 17 00:00:00 2001 From: sagessylu Date: Sun, 15 Aug 2021 22:02:13 +0200 Subject: [PATCH 046/114] Add no-safety-backup option to yunohost app upgrade --- data/actionsmap/yunohost.yml | 4 ++++ src/yunohost/app.py | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 5df1c0877..43955c017 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -693,6 +693,10 @@ app: full: --force help: Force the update, even though the app is up to date action: store_true + -b: + full: --no-safety-backup + help: Disable the safety backup during upgrade + action: store_true ### app_change_url() change-url: diff --git a/src/yunohost/app.py b/src/yunohost/app.py index a48400a8e..1841b2a07 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -502,7 +502,7 @@ def app_change_url(operation_logger, app, domain, path): hook_callback("post_app_change_url", env=env_dict) -def app_upgrade(app=[], url=None, file=None, force=False): +def app_upgrade(app=[], url=None, file=None, force=False, no_safety_backup=False): """ Upgrade app @@ -510,6 +510,7 @@ def app_upgrade(app=[], url=None, file=None, force=False): file -- Folder or tarball for upgrade app -- App(s) to upgrade (default all) url -- Git url to fetch for upgrade + no_safety_backup -- Disable the safety backup during upgrade """ from packaging import version @@ -618,6 +619,7 @@ def app_upgrade(app=[], url=None, file=None, force=False): 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) + env_dict["YNH_APP_NO_BACKUP_UPGRADE"] = no_safety_backup # We'll check that the app didn't brutally edit some system configuration manually_modified_files_before_install = manually_modified_files() From b39201a1933e4f1cf06c0f668a581c032a39e993 Mon Sep 17 00:00:00 2001 From: mifegui Date: Sun, 15 Aug 2021 19:22:04 +0000 Subject: [PATCH 047/114] Translated using Weblate (Portuguese) Currently translated at 8.1% (52 of 637 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/pt/ --- locales/pt.json | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/locales/pt.json b/locales/pt.json index 72d983a39..82edbf349 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -1,7 +1,7 @@ { "action_invalid": "Acção Inválida '{action}'", "admin_password": "Senha de administração", - "admin_password_change_failed": "Não é possível alterar a senha", + "admin_password_change_failed": "Não foi possível alterar a senha", "admin_password_changed": "A senha da administração foi alterada", "app_already_installed": "{app} já está instalada", "app_extraction_failed": "Não foi possível extrair os ficheiros para instalação", @@ -108,12 +108,12 @@ "backup_hook_unknown": "Gancho de backup '{hook}' desconhecido", "backup_nothings_done": "Não há nada para guardar", "backup_output_directory_forbidden": "Diretório de saída proibido. Os backups não podem ser criados em /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ou /home/yunohost.backup/archives subpastas", - "app_already_installed_cant_change_url": "Este aplicativo já está instalado. A URL não pode ser alterada apenas por esta função. Olhe para o `app changeurl` se estiver disponível.", + "app_already_installed_cant_change_url": "Este aplicativo já está instalado. A URL não pode ser alterada apenas por esta função. Confira em `app changeurl` se está disponível.", "app_already_up_to_date": "{app} já está atualizado", - "app_argument_choice_invalid": "Escolha inválida para o argumento '{name}', deve ser um dos {choices}", - "app_argument_invalid": "Valor inválido de argumento '{name}': {error}", + "app_argument_choice_invalid": "Use uma das opções '{choices}' para o argumento '{name}'", + "app_argument_invalid": "Escolha um valor válido para o argumento '{name}': {error}", "app_argument_required": "O argumento '{name}' é obrigatório", - "app_change_url_failed_nginx_reload": "Falha ao reiniciar o nginx. Aqui está o retorno de 'nginx -t':\n{nginx_errors}", + "app_change_url_failed_nginx_reload": "Não foi possível reiniciar o nginx. Aqui está o retorno de 'nginx -t':\n{nginx_errors}", "app_location_unavailable": "Esta url não está disponível ou está em conflito com outra aplicação já instalada", "app_upgrade_app_name": "Atualizando aplicação {app}…", "app_upgrade_some_app_failed": "Não foi possível atualizar algumas aplicações", @@ -129,5 +129,12 @@ "app_change_url_identical_domains": "O antigo e o novo domínio / url_path são idênticos ('{domain}{path}'), nada para fazer.", "password_too_simple_1": "A senha precisa ter pelo menos 8 caracteres", "admin_password_too_long": "Escolha uma senha que contenha menos de 127 caracteres", - "aborting": "Abortando." -} \ No newline at end of file + "aborting": "Abortando.", + "app_change_url_no_script": "A aplicação '{app_name}' ainda não permite modificar a URL. Talvez devesse atualizá-la.", + "app_argument_password_no_default": "Erro ao interpretar argumento da senha '{name}': O argumento da senha não pode ter um valor padrão por segurança", + "app_action_cannot_be_ran_because_required_services_down": "Estes serviços devem estar funcionado para executar esta ação: {services}. Tente reiniciá-los para continuar (e possivelmente investigar o porquê de não estarem funcionado).", + "app_action_broke_system": "Esta ação parece ter quebrado estes serviços importantes: {services}", + "already_up_to_date": "Nada a ser feito. Tudo já está atualizado.", + "additional_urls_already_removed": "A URL adicional '{url}'já está removida para a permissão '{permission}'", + "additional_urls_already_added": "A URL adicional '{url}' já está adicionada para a permissão '{permission}'" +} From ccb6dc54b1091f51e4a3689faf82f686ba4d7d90 Mon Sep 17 00:00:00 2001 From: ljf Date: Tue, 17 Aug 2021 17:26:00 +0200 Subject: [PATCH 048/114] [fix] Avoid warning and use safeloader --- data/actionsmap/yunohost_completion.py | 2 +- data/helpers.d/setting | 4 ++-- data/hooks/conf_regen/01-yunohost | 4 ++-- doc/generate_manpages.py | 2 +- src/yunohost/app.py | 4 ++-- src/yunohost/firewall.py | 2 +- src/yunohost/regenconf.py | 2 +- src/yunohost/service.py | 2 +- tests/test_actionmap.py | 2 +- tests/test_i18n_keys.py | 2 +- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/data/actionsmap/yunohost_completion.py b/data/actionsmap/yunohost_completion.py index bc32028d3..3891aee9c 100644 --- a/data/actionsmap/yunohost_completion.py +++ b/data/actionsmap/yunohost_completion.py @@ -32,7 +32,7 @@ def get_dict_actions(OPTION_SUBTREE, category): with open(ACTIONSMAP_FILE, "r") as stream: # Getting the dictionary containning what actions are possible per category - OPTION_TREE = yaml.load(stream) + OPTION_TREE = yaml.safe_load(stream) CATEGORY = [ category for category in OPTION_TREE.keys() if not category.startswith("_") diff --git a/data/helpers.d/setting b/data/helpers.d/setting index 2950b3829..66bce9717 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -86,7 +86,7 @@ key, value = os.environ['KEY'], os.environ.get('VALUE', None) setting_file = "/etc/yunohost/apps/%s/settings.yml" % app assert os.path.exists(setting_file), "Setting file %s does not exists ?" % setting_file with open(setting_file) as f: - settings = yaml.load(f) + settings = yaml.safe_load(f) if action == "get": if key in settings: print(settings[key]) @@ -96,7 +96,7 @@ else: del settings[key] elif action == "set": if key in ['redirected_urls', 'redirected_regex']: - value = yaml.load(value) + value = yaml.safe_load(value) settings[key] = value else: raise ValueError("action should either be get, set or delete") diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index 8ef398f1d..1a10a6954 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -212,10 +212,10 @@ import yaml with open('services.yml') as f: - new_services = yaml.load(f) + new_services = yaml.safe_load(f) with open('/etc/yunohost/services.yml') as f: - services = yaml.load(f) or {} + services = yaml.safe_load(f) or {} updated = False diff --git a/doc/generate_manpages.py b/doc/generate_manpages.py index f681af7dd..8691bab37 100644 --- a/doc/generate_manpages.py +++ b/doc/generate_manpages.py @@ -33,7 +33,7 @@ def ordered_yaml_load(stream): yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, lambda loader, node: OrderedDict(loader.construct_pairs(node)), ) - return yaml.load(stream, OrderedLoader) + return yaml.safe_load(stream, OrderedLoader) def main(): diff --git a/src/yunohost/app.py b/src/yunohost/app.py index a48400a8e..6a2640cee 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1518,7 +1518,7 @@ def app_setting(app, key, value=None, delete=False): # SET else: if key in ["redirected_urls", "redirected_regex"]: - value = yaml.load(value) + value = yaml.safe_load(value) app_settings[key] = value _set_app_settings(app, app_settings) @@ -2175,7 +2175,7 @@ def _get_app_settings(app_id): ) try: with open(os.path.join(APPS_SETTING_PATH, app_id, "settings.yml")) as f: - settings = yaml.load(f) + settings = yaml.safe_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: v for k, v in settings.items()} diff --git a/src/yunohost/firewall.py b/src/yunohost/firewall.py index 9850defa5..4be6810ec 100644 --- a/src/yunohost/firewall.py +++ b/src/yunohost/firewall.py @@ -179,7 +179,7 @@ def firewall_list(raw=False, by_ip_version=False, list_forwarded=False): """ with open(FIREWALL_FILE) as f: - firewall = yaml.load(f) + firewall = yaml.safe_load(f) if raw: return firewall diff --git a/src/yunohost/regenconf.py b/src/yunohost/regenconf.py index 924818e44..0608bcf8c 100644 --- a/src/yunohost/regenconf.py +++ b/src/yunohost/regenconf.py @@ -444,7 +444,7 @@ def _get_regenconf_infos(): """ try: with open(REGEN_CONF_FILE, "r") as f: - return yaml.load(f) + return yaml.safe_load(f) except Exception: return {} diff --git a/src/yunohost/service.py b/src/yunohost/service.py index e6e960a57..912662600 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -670,7 +670,7 @@ def _get_services(): """ try: with open("/etc/yunohost/services.yml", "r") as f: - services = yaml.load(f) or {} + services = yaml.safe_load(f) or {} except Exception: return {} diff --git a/tests/test_actionmap.py b/tests/test_actionmap.py index bf6755979..0b8abb152 100644 --- a/tests/test_actionmap.py +++ b/tests/test_actionmap.py @@ -2,4 +2,4 @@ import yaml def test_yaml_syntax(): - yaml.load(open("data/actionsmap/yunohost.yml")) + yaml.safe_load(open("data/actionsmap/yunohost.yml")) diff --git a/tests/test_i18n_keys.py b/tests/test_i18n_keys.py index 2ad56a34e..7b5ad1956 100644 --- a/tests/test_i18n_keys.py +++ b/tests/test_i18n_keys.py @@ -108,7 +108,7 @@ def find_expected_string_keys(): yield m # Keys for the actionmap ... - for category in yaml.load(open("data/actionsmap/yunohost.yml")).values(): + for category in yaml.safe_load(open("data/actionsmap/yunohost.yml")).values(): if "actions" not in category.keys(): continue for action in category["actions"].values(): From 5518931c029f60aaaffc9e38e89b20b7574f8b3b Mon Sep 17 00:00:00 2001 From: ljf Date: Tue, 17 Aug 2021 17:33:23 +0200 Subject: [PATCH 049/114] [fix] OrderedLoader from SafeLoader --- doc/generate_manpages.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/generate_manpages.py b/doc/generate_manpages.py index 8691bab37..fa042fb17 100644 --- a/doc/generate_manpages.py +++ b/doc/generate_manpages.py @@ -26,14 +26,14 @@ ACTIONSMAP_FILE = os.path.join(THIS_SCRIPT_DIR, "../data/actionsmap/yunohost.yml def ordered_yaml_load(stream): - class OrderedLoader(yaml.Loader): + class OrderedLoader(yaml.SafeLoader): pass OrderedLoader.add_constructor( yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, lambda loader, node: OrderedDict(loader.construct_pairs(node)), ) - return yaml.safe_load(stream, OrderedLoader) + return yaml.load(stream, OrderedLoader) def main(): From 69a5ae5736f3e89b0a7d5cfec7a6106a71187b40 Mon Sep 17 00:00:00 2001 From: sagessylu <49091098+sagessylu@users.noreply.github.com> Date: Tue, 17 Aug 2021 20:27:33 +0200 Subject: [PATCH 050/114] Update src/yunohost/app.py Co-authored-by: ljf (zamentur) --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 1841b2a07..a69229ad4 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -619,7 +619,7 @@ def app_upgrade(app=[], url=None, file=None, force=False, no_safety_backup=False 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) - env_dict["YNH_APP_NO_BACKUP_UPGRADE"] = no_safety_backup + env_dict["NO_BACKUP_UPGRADE"] = no_safety_backup # We'll check that the app didn't brutally edit some system configuration manually_modified_files_before_install = manually_modified_files() From 09efb86ff10ab4786cdf6a0350435832120ea9e4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 18 Aug 2021 19:40:10 +0200 Subject: [PATCH 051/114] Improve help text for app remove --purge --- data/actionsmap/yunohost.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index bd9207528..bcd6a61c3 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -676,7 +676,7 @@ app: help: App to remove -p: full: --purge - help: Remove with all app data + help: Also remove all application data action: store_true ### app_upgrade() From 4b84922315e9e89e4d92420d6c8bb95d20b4749c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 18 Aug 2021 19:47:46 +0200 Subject: [PATCH 052/114] Apply suggestions from code review --- data/helpers.d/multimedia | 6 +++--- data/hooks/post_user_create/ynh_multimedia | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/helpers.d/multimedia b/data/helpers.d/multimedia index 4ec7611fc..552b8c984 100644 --- a/data/helpers.d/multimedia +++ b/data/helpers.d/multimedia @@ -32,9 +32,9 @@ ynh_multimedia_build_main_dir() { ln -sfn "$MEDIA_DIRECTORY/share" "$MEDIA_DIRECTORY/$user/Share" # Création du lien symbolique dans le home de l'utilisateur. #link will only be created if the home directory of the user exists and if it's located in '/home' folder - home="$(getent passwd $user | cut -d: -f6)" - if [[ -d "$home" && "$(echo "$home" | grep /home/)" ]]; then - ln -sfn "$MEDIA_DIRECTORY/$user" "$home/Multimedia" + local user_home="$(getent passwd $user | cut -d: -f6 | grep '^/home/')" + if [[ -d "$user_home" ]]; then + ln -sfn "$MEDIA_DIRECTORY/$user" "$user_home/Multimedia" fi # Propriétaires des dossiers utilisateurs. chown -R $user "$MEDIA_DIRECTORY/$user" diff --git a/data/hooks/post_user_create/ynh_multimedia b/data/hooks/post_user_create/ynh_multimedia index 2fa02505a..2be3f42d4 100644 --- a/data/hooks/post_user_create/ynh_multimedia +++ b/data/hooks/post_user_create/ynh_multimedia @@ -16,9 +16,9 @@ mkdir -p "$MEDIA_DIRECTORY/$user/eBook" ln -sfn "$MEDIA_DIRECTORY/share" "$MEDIA_DIRECTORY/$user/Share" # Création du lien symbolique dans le home de l'utilisateur. #link will only be created if the home directory of the user exists and if it's located in '/home' folder -home="$(getent passwd $user | cut -d: -f6)" -if [[ -d "$home" && "$(echo "$home" | grep /home/)" ]]; then - ln -sfn "$MEDIA_DIRECTORY/$user" "$home/Multimedia" +local user_home="$(getent passwd $user | cut -d: -f6 | grep '^/home/')" +if [[ -d "$user_home" ]]; then + ln -sfn "$MEDIA_DIRECTORY/$user" "$user_home/Multimedia" fi # Propriétaires des dossiers utilisateurs. chown -R $user "$MEDIA_DIRECTORY/$user" From bcb803c0c37b8b3a540ad4d4c5267c3448edba8e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 18 Aug 2021 22:10:25 +0200 Subject: [PATCH 053/114] Add new setting to enable experimental security features --- data/hooks/conf_regen/01-yunohost | 15 +++++++++++++++ data/hooks/conf_regen/15-nginx | 1 + data/templates/nginx/security.conf.inc | 10 ++++++++++ data/templates/yunohost/proc-hidepid.service | 14 ++++++++++++++ locales/en.json | 1 + src/yunohost/settings.py | 7 +++++++ 6 files changed, 48 insertions(+) create mode 100644 data/templates/yunohost/proc-hidepid.service diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index 1a10a6954..8d2280e89 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -144,6 +144,14 @@ HandleLidSwitch=ignore HandleLidSwitchDocked=ignore HandleLidSwitchExternalPower=ignore EOF + + mkdir -p ${pending_dir}/etc/systemd/ + if [[ "$(yunohost settings get 'security.experimental.enabled')" == "True" ]] + then + cp proc-hidepid.service ${pending_dir}/etc/systemd/system/proc-hidepid.service + else + touch ${pending_dir}/etc/systemd/system/proc-hidepid.service + fi } @@ -204,6 +212,13 @@ do_post_regen() { # Propagates changes in systemd service config overrides [[ ! "$regen_conf_files" =~ "ntp.service.d/ynh-override.conf" ]] || { systemctl daemon-reload; systemctl restart ntp; } [[ ! "$regen_conf_files" =~ "nftables.service.d/ynh-override.conf" ]] || systemctl daemon-reload + [[ ! "$regen_conf_files" =~ "login.conf.d/ynh-override.conf" ]] || systemctl daemon-reload + if [[ "$regen_conf_files" =~ "proc-hidepid.service" ]] + then + systemctl daemon-reload + action=$([[ -e /etc/systemd/system/proc-hidepid.service ]] && echo 'enable' || echo 'disable') + systemctl $action proc-hidepid --quiet --now + fi } _update_services() { diff --git a/data/hooks/conf_regen/15-nginx b/data/hooks/conf_regen/15-nginx index e211a3aca..a2d8f1259 100755 --- a/data/hooks/conf_regen/15-nginx +++ b/data/hooks/conf_regen/15-nginx @@ -61,6 +61,7 @@ do_pre_regen() { # Support different strategy for security configurations export compatibility="$(yunohost settings get 'security.nginx.compatibility')" + export experimental="$(yunohost settings get 'security.experimental.enabled')" ynh_render_template "security.conf.inc" "${nginx_conf_dir}/security.conf.inc" cert_status=$(yunohost domain cert-status --json) diff --git a/data/templates/nginx/security.conf.inc b/data/templates/nginx/security.conf.inc index 0d0b74db1..bcb821770 100644 --- a/data/templates/nginx/security.conf.inc +++ b/data/templates/nginx/security.conf.inc @@ -25,7 +25,11 @@ ssl_dhparam /usr/share/yunohost/other/ffdhe2048.pem; # Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners # https://wiki.mozilla.org/Security/Guidelines/Web_Security # https://observatory.mozilla.org/ +{% if experimental == "True" %} +more_set_headers "Content-Security-Policy : upgrade-insecure-requests; default-src https: data:"; +{% else %} more_set_headers "Content-Security-Policy : upgrade-insecure-requests"; +{% endif %} more_set_headers "Content-Security-Policy-Report-Only : default-src https: data: 'unsafe-inline' 'unsafe-eval' "; more_set_headers "X-Content-Type-Options : nosniff"; more_set_headers "X-XSS-Protection : 1; mode=block"; @@ -34,7 +38,13 @@ more_set_headers "X-Permitted-Cross-Domain-Policies : none"; more_set_headers "X-Frame-Options : SAMEORIGIN"; # Disable the disaster privacy thing that is FLoC +{% if experimental == "True" %} +more_set_headers "Permissions-Policy : fullscreen=(), geolocation=(), payment=(), accelerometer=(), battery=(), magnetometer=(), usb=(), interest-cohort=()"; +# Force HTTPOnly and Secure for all cookies +proxy_cookie_path ~$ "; HTTPOnly; Secure;"; +{% else %} more_set_headers "Permissions-Policy : interest-cohort=()"; +{% endif %} # Disable gzip to protect against BREACH # Read https://trac.nginx.org/nginx/ticket/1720 (text/html cannot be disabled!) diff --git a/data/templates/yunohost/proc-hidepid.service b/data/templates/yunohost/proc-hidepid.service new file mode 100644 index 000000000..ec6fabede --- /dev/null +++ b/data/templates/yunohost/proc-hidepid.service @@ -0,0 +1,14 @@ +[Unit] +Description=Mounts /proc with hidepid=2 +DefaultDependencies=no +Before=sysinit.target +Requires=local-fs.target +After=local-fs.target + +[Service] +Type=oneshot +ExecStart=/bin/mount -o remount,nosuid,nodev,noexec,hidepid=2 /proc +RemainAfterExit=yes + +[Install] +WantedBy=sysinit.target diff --git a/locales/en.json b/locales/en.json index 693e9d24d..044461d9a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -340,6 +340,7 @@ "global_settings_setting_smtp_relay_password": "SMTP relay host password", "global_settings_setting_security_webadmin_allowlist_enabled": "Allow only some IPs to access the webadmin.", "global_settings_setting_security_webadmin_allowlist": "IP adresses allowed to access the webadmin. Comma-separated.", + "global_settings_setting_security_experimental_enabled": "Enable experimental security features (don't enable this if you don't know what you're doing!)", "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} appears to have the type {unknown_type} 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).", diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index ec0e7566c..fe072cddb 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -102,6 +102,7 @@ DEFAULTS = OrderedDict( ("ssowat.panel_overlay.enabled", {"type": "bool", "default": True}), ("security.webadmin.allowlist.enabled", {"type": "bool", "default": False}), ("security.webadmin.allowlist", {"type": "string", "default": ""}), + ("security.experimental.enabled", {"type": "bool", "default": False}), ] ) @@ -399,6 +400,12 @@ def reconfigure_nginx(setting_name, old_value, new_value): regen_conf(names=["nginx"]) +@post_change_hook("security.experimental.enabled") +def reconfigure_nginx_and_yunohost(setting_name, old_value, new_value): + if old_value != new_value: + regen_conf(names=["nginx", "yunohost"]) + + @post_change_hook("security.ssh.compatibility") def reconfigure_ssh(setting_name, old_value, new_value): if old_value != new_value: From a29940d8f46c5c96318cc2a724cccfe079559738 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 18 Aug 2021 23:07:38 +0200 Subject: [PATCH 054/114] Trash ugly hack that merge services.yml every regenconf --- data/hooks/conf_regen/01-yunohost | 88 +++---------------------------- src/yunohost/service.py | 59 +++++++++++++++------ 2 files changed, 49 insertions(+), 98 deletions(-) diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index 1a10a6954..97cdb0b40 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -2,8 +2,6 @@ set -e -services_path="/etc/yunohost/services.yml" - do_init_regen() { if [[ $EUID -ne 0 ]]; then echo "You must be root to run this script" 1>&2 @@ -19,8 +17,6 @@ do_init_regen() { || echo "yunohost.org" > /etc/yunohost/current_host # copy default services and firewall - [[ -f $services_path ]] \ - || cp services.yml "$services_path" [[ -f /etc/yunohost/firewall.yml ]] \ || cp firewall.yml /etc/yunohost/firewall.yml @@ -49,6 +45,9 @@ do_init_regen() { chmod 644 /etc/ssowat/conf.json.persistent chown root:root /etc/ssowat/conf.json.persistent + # Empty service conf + touch /etc/yunohost/services.yml + mkdir -p /var/cache/yunohost/repo chown root:root /var/cache/yunohost chmod 700 /var/cache/yunohost @@ -59,25 +58,9 @@ do_pre_regen() { cd /usr/share/yunohost/templates/yunohost - # update services.yml - if [[ -f $services_path ]]; then - tmp_services_path="${services_path}-tmp" - new_services_path="${services_path}-new" - cp "$services_path" "$tmp_services_path" - _update_services "$new_services_path" || { - mv "$tmp_services_path" "$services_path" - exit 1 - } - if [[ -f $new_services_path ]]; then - # replace services.yml with new one - mv "$new_services_path" "$services_path" - mv "$tmp_services_path" "${services_path}-old" - else - rm -f "$tmp_services_path" - fi - else - cp services.yml /etc/yunohost/services.yml - fi + # Legacy code that can be removed once on bullseye + touch /etc/yunohost/services.yml + yunohost tools shell -c "from yunohost.service import _get_services, _save_services; _save_services(_get_services())" mkdir -p $pending_dir/etc/cron.d/ mkdir -p $pending_dir/etc/cron.daily/ @@ -206,65 +189,6 @@ do_post_regen() { [[ ! "$regen_conf_files" =~ "nftables.service.d/ynh-override.conf" ]] || systemctl daemon-reload } -_update_services() { - python3 - << EOF -import yaml - - -with open('services.yml') as f: - new_services = yaml.safe_load(f) - -with open('/etc/yunohost/services.yml') as f: - services = yaml.safe_load(f) or {} - -updated = False - - -for service, conf in new_services.items(): - # remove service with empty conf - if conf is None: - if service in services: - print("removing '{0}' from services".format(service)) - del services[service] - updated = True - - # add new service - elif not services.get(service, None): - print("adding '{0}' to services".format(service)) - services[service] = conf - updated = True - - # update service conf - else: - conffiles = services[service].pop('conffiles', {}) - - # status need to be removed - if "status" not in conf and "status" in services[service]: - print("update '{0}' service status access".format(service)) - del services[service]["status"] - updated = True - - if services[service] != conf: - print("update '{0}' service".format(service)) - services[service].update(conf) - updated = True - - if conffiles: - services[service]['conffiles'] = conffiles - - # Remove legacy /var/log/daemon.log and /var/log/syslog from log entries - # because they are too general. Instead, now the journalctl log is - # returned by default which is more relevant. - if "log" in services[service]: - if services[service]["log"] in ["/var/log/syslog", "/var/log/daemon.log"]: - del services[service]["log"] - -if updated: - with open('/etc/yunohost/services.yml-new', 'w') as f: - yaml.safe_dump(services, f, default_flow_style=False) -EOF -} - FORCE=${2:-0} DRY_RUN=${3:-0} diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 912662600..671447067 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -37,10 +37,13 @@ from moulinette import m18n from yunohost.utils.error import YunohostError, YunohostValidationError from moulinette.utils.process import check_output from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import read_file, append_to_file, write_to_file +from moulinette.utils.filesystem import read_file, append_to_file, write_to_file, read_yaml, write_to_yaml MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock" +SERVICES_CONF = "/etc/yunohost/services.yml" +SERVICES_CONF_BASE = "/usr/share/yunohost/templates/yunohost/services.yml" + logger = getActionLogger("yunohost.service") @@ -127,7 +130,8 @@ def service_add( try: _save_services(services) - except Exception: + except Exception as e: + logger.warning(e) # we'll get a logger.warning with more details in _save_services raise YunohostError("service_add_failed", service=name) @@ -669,17 +673,19 @@ def _get_services(): """ try: - with open("/etc/yunohost/services.yml", "r") as f: - services = yaml.safe_load(f) or {} + services = read_yaml(SERVICES_CONF_BASE) or {} + + # These are keys flagged 'null' in the base conf + legacy_keys_to_delete = [k for k, v in services.items() if v is None] + + services.update(read_yaml(SERVICES_CONF) or {}) + + services = {name: infos + for name, infos in services.items() + if name not in legacy_keys_to_delete} except Exception: return {} - # some services are marked as None to remove them from YunoHost - # filter this - for key, value in list(services.items()): - if value is None: - del services[key] - # Dirty hack to automatically find custom SSH port ... ssh_port_line = re.findall( r"\bPort *([0-9]{2,5})\b", read_file("/etc/ssh/sshd_config") @@ -703,6 +709,13 @@ def _get_services(): del services["postgresql"]["description"] services["postgresql"]["actual_systemd_service"] = "postgresql@11-main" + # Remove legacy /var/log/daemon.log and /var/log/syslog from log entries + # because they are too general. Instead, now the journalctl log is + # returned by default which is more relevant. + for infos in services.values(): + if infos.get("log") in ["/var/log/syslog", "/var/log/daemon.log"]: + del infos["log"] + return services @@ -714,12 +727,26 @@ def _save_services(services): services -- A dict of managed services with their parameters """ - try: - with open("/etc/yunohost/services.yml", "w") as f: - yaml.safe_dump(services, f, default_flow_style=False) - except Exception as e: - logger.warning("Error while saving services, exception: %s", e, exc_info=1) - raise + + # Compute the diff with the base file + # such that /etc/yunohost/services.yml contains the minimal + # changes with respect to the base conf + + conf_base = yaml.safe_load(open(SERVICES_CONF_BASE)) or {} + + diff = {} + + for service_name, service_infos in services.items(): + service_conf_base = conf_base.get(service_name, {}) + diff[service_name] = {} + + for key, value in service_infos.items(): + if service_conf_base.get(key) != value: + diff[service_name][key] = value + + diff = {name: infos for name, infos in diff.items() if infos} + + write_to_yaml(SERVICES_CONF, diff) def _tail(file, n): From 3c07e55017d2d2c42a45c9aea027396f1121913f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 19 Aug 2021 18:14:15 +0200 Subject: [PATCH 055/114] [fix] hook_exec / subprocess.Popen explodes if you feed non-string values in env variables --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index ef753d4d6..fad2033a6 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1233,7 +1233,7 @@ def app_remove(operation_logger, app, purge=False): env_dict["YNH_APP_INSTANCE_NAME"] = app env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?") - env_dict["YNH_APP_PURGE"] = purge + env_dict["YNH_APP_PURGE"] = str(purge) operation_logger.extra.update({"env": env_dict}) operation_logger.flush() From e3f11bec0982e3ba9e3b9800a4acc1d104c56107 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 19 Aug 2021 18:41:47 +0200 Subject: [PATCH 056/114] NO_BACKUP_UPGRADE: helpers expect 0 or 1 --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index fad2033a6..57498c644 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -619,7 +619,7 @@ def app_upgrade(app=[], url=None, file=None, force=False, no_safety_backup=False 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) - env_dict["NO_BACKUP_UPGRADE"] = no_safety_backup + env_dict["NO_BACKUP_UPGRADE"] = "1" if no_safety_backup else "0" # We'll check that the app didn't brutally edit some system configuration manually_modified_files_before_install = manually_modified_files() From b452838a1797641f82778e60f4e50e1722837eba Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 19 Aug 2021 19:16:29 +0200 Subject: [PATCH 057/114] Update changelog for 4.2.8 --- debian/changelog | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/debian/changelog b/debian/changelog index 57da44532..b1894758a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,16 @@ +yunohost (4.2.8) stable; urgency=low + + - [fix] ynh_permission_has_user not behaving properly when checking if a group is allowed (f0590907) + - [enh] use yaml safeloader everywhere ([#1287](https://github.com/YunoHost/yunohost/pull/1287)) + - [enh] Add --no-safety-backup option to "yunohost app upgrade" ([#1286](https://github.com/YunoHost/yunohost/pull/1286)) + - [enh] Add --purge option to "yunohost app remove" ([#1285](https://github.com/YunoHost/yunohost/pull/1285)) + - [enh] Multimedia helper: check that home folder exists ([#1255](https://github.com/YunoHost/yunohost/pull/1255)) + - [i18n] Translations updated for French, Galician, German, Portuguese + + Thanks to all contributors <3 ! (José M, Kay0u, Krakinou, ljf, Luca, mifegui, ppr, sagessylu) + + -- Alexandre Aubin Thu, 19 Aug 2021 19:11:19 +0200 + yunohost (4.2.7) stable; urgency=low Notable changes: From ecf136d5db27caae21656091b948eb825369b248 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 19 Aug 2021 20:28:20 +0200 Subject: [PATCH 058/114] Auto-enable yunomdns on legacy systems --- data/hooks/conf_regen/37-mdns | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/hooks/conf_regen/37-mdns b/data/hooks/conf_regen/37-mdns index c85e43a9a..1d7381e26 100755 --- a/data/hooks/conf_regen/37-mdns +++ b/data/hooks/conf_regen/37-mdns @@ -51,6 +51,12 @@ do_post_regen() { systemctl daemon-reload fi + # Legacy stuff to enable the new yunomdns service on legacy systems + if [[ -e /etc/avahi/avahi-daemon.conf ]] && grep -q 'yunohost' /etc/avahi/avahi-daemon.conf + then + systemctl enable yunomdns + fi + [[ -z "$regen_conf_files" ]] \ || systemctl restart yunomdns } From b8c8ac0b2de6e2a5c91f2c50dab54209ec619faa Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 19 Aug 2021 20:29:28 +0200 Subject: [PATCH 059/114] Gotta also remove libnss-mdns if we want to get rid of avahi-daemon --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index c9306bef1..37eccb5dd 100644 --- a/debian/control +++ b/debian/control @@ -21,7 +21,7 @@ Depends: ${python3:Depends}, ${misc:Depends} , openssh-server, iptables, fail2ban, dnsutils, bind9utils , openssl, ca-certificates, netcat-openbsd, iproute2 , slapd, ldap-utils, sudo-ldap, libnss-ldapd, unscd, libpam-ldapd - , dnsmasq, libnss-mdns, resolvconf, libnss-myhostname + , dnsmasq, resolvconf, libnss-myhostname , postfix, postfix-ldap, postfix-policyd-spf-perl, postfix-pcre , dovecot-core, dovecot-ldap, dovecot-lmtpd, dovecot-managesieved, dovecot-antispam , rspamd, opendkim-tools, postsrsd, procmail, mailutils From 5249be031f1ea6d7e72d406487839e3d082c0b8f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 20 Aug 2021 10:12:35 +0200 Subject: [PATCH 060/114] Propagate msignal change --- src/yunohost/tests/conftest.py | 5 + .../tests/test_apps_arguments_parsing.py | 102 +++++++++--------- 2 files changed, 56 insertions(+), 51 deletions(-) diff --git a/src/yunohost/tests/conftest.py b/src/yunohost/tests/conftest.py index 1bf035748..9dfe2b39c 100644 --- a/src/yunohost/tests/conftest.py +++ b/src/yunohost/tests/conftest.py @@ -82,5 +82,10 @@ def pytest_cmdline_main(config): yunohost.init(debug=config.option.yunodebug) class DummyInterface(): + type = "test" + + def prompt(*args, **kwargs): + raise NotImplementedError + Moulinette._interface = DummyInterface() diff --git a/src/yunohost/tests/test_apps_arguments_parsing.py b/src/yunohost/tests/test_apps_arguments_parsing.py index 95d1548ae..573c18cb2 100644 --- a/src/yunohost/tests/test_apps_arguments_parsing.py +++ b/src/yunohost/tests/test_apps_arguments_parsing.py @@ -5,7 +5,7 @@ from mock import patch from io import StringIO from collections import OrderedDict -from moulinette import msignals +from moulinette import Moulinette from yunohost import domain, user from yunohost.app import _parse_args_in_yunohost_format, PasswordArgumentParser @@ -84,7 +84,7 @@ def test_parse_args_in_yunohost_format_string_input(): answers = {} expected_result = OrderedDict({"some_string": ("some_value", "string")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -97,7 +97,7 @@ def test_parse_args_in_yunohost_format_string_input_no_ask(): answers = {} expected_result = OrderedDict({"some_string": ("some_value", "string")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -124,7 +124,7 @@ def test_parse_args_in_yunohost_format_string_optional_with_input(): answers = {} expected_result = OrderedDict({"some_string": ("some_value", "string")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -139,7 +139,7 @@ def test_parse_args_in_yunohost_format_string_optional_with_empty_input(): answers = {} expected_result = OrderedDict({"some_string": ("", "string")}) - with patch.object(msignals, "prompt", return_value=""): + with patch.object(Moulinette.interface, "prompt", return_value=""): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -153,7 +153,7 @@ def test_parse_args_in_yunohost_format_string_optional_with_input_without_ask(): answers = {} expected_result = OrderedDict({"some_string": ("some_value", "string")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -180,7 +180,7 @@ def test_parse_args_in_yunohost_format_string_input_test_ask(): ] answers = {} - with patch.object(msignals, "prompt", return_value="some_value") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with(ask_text, False) @@ -197,7 +197,7 @@ def test_parse_args_in_yunohost_format_string_input_test_ask_with_default(): ] answers = {} - with patch.object(msignals, "prompt", return_value="some_value") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with("%s (default: %s)" % (ask_text, default_text), False) @@ -215,7 +215,7 @@ def test_parse_args_in_yunohost_format_string_input_test_ask_with_example(): ] answers = {} - with patch.object(msignals, "prompt", return_value="some_value") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert example_text in prompt.call_args[0][0] @@ -234,7 +234,7 @@ def test_parse_args_in_yunohost_format_string_input_test_ask_with_help(): ] answers = {} - with patch.object(msignals, "prompt", return_value="some_value") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert help_text in prompt.call_args[0][0] @@ -251,7 +251,7 @@ def test_parse_args_in_yunohost_format_string_with_choice_prompt(): questions = [{"name": "some_string", "type": "string", "choices": ["fr", "en"]}] answers = {"some_string": "fr"} expected_result = OrderedDict({"some_string": ("fr", "string")}) - with patch.object(msignals, "prompt", return_value="fr"): + with patch.object(Moulinette.interface, "prompt", return_value="fr"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -275,7 +275,7 @@ def test_parse_args_in_yunohost_format_string_with_choice_ask(): ] answers = {} - with patch.object(msignals, "prompt", return_value="ru") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="ru") as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] @@ -333,7 +333,7 @@ def test_parse_args_in_yunohost_format_password_input(): answers = {} expected_result = OrderedDict({"some_password": ("some_value", "password")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -347,7 +347,7 @@ def test_parse_args_in_yunohost_format_password_input_no_ask(): answers = {} expected_result = OrderedDict({"some_password": ("some_value", "password")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -383,7 +383,7 @@ def test_parse_args_in_yunohost_format_password_optional_with_input(): answers = {} expected_result = OrderedDict({"some_password": ("some_value", "password")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -399,7 +399,7 @@ def test_parse_args_in_yunohost_format_password_optional_with_empty_input(): answers = {} expected_result = OrderedDict({"some_password": ("", "password")}) - with patch.object(msignals, "prompt", return_value=""): + with patch.object(Moulinette.interface, "prompt", return_value=""): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -414,7 +414,7 @@ def test_parse_args_in_yunohost_format_password_optional_with_input_without_ask( answers = {} expected_result = OrderedDict({"some_password": ("some_value", "password")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -462,7 +462,7 @@ def test_parse_args_in_yunohost_format_password_input_test_ask(): ] answers = {} - with patch.object(msignals, "prompt", return_value="some_value") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with(ask_text, True) @@ -481,7 +481,7 @@ def test_parse_args_in_yunohost_format_password_input_test_ask_with_example(): ] answers = {} - with patch.object(msignals, "prompt", return_value="some_value") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert example_text in prompt.call_args[0][0] @@ -501,7 +501,7 @@ def test_parse_args_in_yunohost_format_password_input_test_ask_with_help(): ] answers = {} - with patch.object(msignals, "prompt", return_value="some_value") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert help_text in prompt.call_args[0][0] @@ -594,7 +594,7 @@ def test_parse_args_in_yunohost_format_path_input(): answers = {} expected_result = OrderedDict({"some_path": ("some_value", "path")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -608,7 +608,7 @@ def test_parse_args_in_yunohost_format_path_input_no_ask(): answers = {} expected_result = OrderedDict({"some_path": ("some_value", "path")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -637,7 +637,7 @@ def test_parse_args_in_yunohost_format_path_optional_with_input(): answers = {} expected_result = OrderedDict({"some_path": ("some_value", "path")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -653,7 +653,7 @@ def test_parse_args_in_yunohost_format_path_optional_with_empty_input(): answers = {} expected_result = OrderedDict({"some_path": ("", "path")}) - with patch.object(msignals, "prompt", return_value=""): + with patch.object(Moulinette.interface, "prompt", return_value=""): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -668,7 +668,7 @@ def test_parse_args_in_yunohost_format_path_optional_with_input_without_ask(): answers = {} expected_result = OrderedDict({"some_path": ("some_value", "path")}) - with patch.object(msignals, "prompt", return_value="some_value"): + with patch.object(Moulinette.interface, "prompt", return_value="some_value"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -697,7 +697,7 @@ def test_parse_args_in_yunohost_format_path_input_test_ask(): ] answers = {} - with patch.object(msignals, "prompt", return_value="some_value") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with(ask_text, False) @@ -715,7 +715,7 @@ def test_parse_args_in_yunohost_format_path_input_test_ask_with_default(): ] answers = {} - with patch.object(msignals, "prompt", return_value="some_value") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with("%s (default: %s)" % (ask_text, default_text), False) @@ -734,7 +734,7 @@ def test_parse_args_in_yunohost_format_path_input_test_ask_with_example(): ] answers = {} - with patch.object(msignals, "prompt", return_value="some_value") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert example_text in prompt.call_args[0][0] @@ -754,7 +754,7 @@ def test_parse_args_in_yunohost_format_path_input_test_ask_with_help(): ] answers = {} - with patch.object(msignals, "prompt", return_value="some_value") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert help_text in prompt.call_args[0][0] @@ -918,11 +918,11 @@ def test_parse_args_in_yunohost_format_boolean_input(): answers = {} expected_result = OrderedDict({"some_boolean": (1, "boolean")}) - with patch.object(msignals, "prompt", return_value="y"): + with patch.object(Moulinette.interface, "prompt", return_value="y"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result expected_result = OrderedDict({"some_boolean": (0, "boolean")}) - with patch.object(msignals, "prompt", return_value="n"): + with patch.object(Moulinette.interface, "prompt", return_value="n"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -936,7 +936,7 @@ def test_parse_args_in_yunohost_format_boolean_input_no_ask(): answers = {} expected_result = OrderedDict({"some_boolean": (1, "boolean")}) - with patch.object(msignals, "prompt", return_value="y"): + with patch.object(Moulinette.interface, "prompt", return_value="y"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -965,7 +965,7 @@ def test_parse_args_in_yunohost_format_boolean_optional_with_input(): answers = {} expected_result = OrderedDict({"some_boolean": (1, "boolean")}) - with patch.object(msignals, "prompt", return_value="y"): + with patch.object(Moulinette.interface, "prompt", return_value="y"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -981,7 +981,7 @@ def test_parse_args_in_yunohost_format_boolean_optional_with_empty_input(): answers = {} expected_result = OrderedDict({"some_boolean": (0, "boolean")}) # default to false - with patch.object(msignals, "prompt", return_value=""): + with patch.object(Moulinette.interface, "prompt", return_value=""): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -996,7 +996,7 @@ def test_parse_args_in_yunohost_format_boolean_optional_with_input_without_ask() answers = {} expected_result = OrderedDict({"some_boolean": (0, "boolean")}) - with patch.object(msignals, "prompt", return_value="n"): + with patch.object(Moulinette.interface, "prompt", return_value="n"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -1039,7 +1039,7 @@ def test_parse_args_in_yunohost_format_boolean_input_test_ask(): ] answers = {} - with patch.object(msignals, "prompt", return_value=0) as prompt: + with patch.object(Moulinette.interface, "prompt", return_value=0) as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with(ask_text + " [yes | no] (default: no)", False) @@ -1057,7 +1057,7 @@ def test_parse_args_in_yunohost_format_boolean_input_test_ask_with_default(): ] answers = {} - with patch.object(msignals, "prompt", return_value=1) as prompt: + with patch.object(Moulinette.interface, "prompt", return_value=1) as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with("%s [yes | no] (default: yes)" % ask_text, False) @@ -1193,11 +1193,11 @@ def test_parse_args_in_yunohost_format_domain_two_domains_default_input(): domain, "_get_maindomain", return_value=main_domain ), patch.object(domain, "domain_list", return_value={"domains": domains}): expected_result = OrderedDict({"some_domain": (main_domain, "domain")}) - with patch.object(msignals, "prompt", return_value=main_domain): + with patch.object(Moulinette.interface, "prompt", return_value=main_domain): assert _parse_args_in_yunohost_format(answers, questions) == expected_result expected_result = OrderedDict({"some_domain": (other_domain, "domain")}) - with patch.object(msignals, "prompt", return_value=other_domain): + with patch.object(Moulinette.interface, "prompt", return_value=other_domain): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -1380,14 +1380,14 @@ def test_parse_args_in_yunohost_format_user_two_users_default_input(): with patch.object(user, "user_list", return_value={"users": users}): with patch.object(user, "user_info", return_value={}): expected_result = OrderedDict({"some_user": (username, "user")}) - with patch.object(msignals, "prompt", return_value=username): + with patch.object(Moulinette.interface, "prompt", return_value=username): assert ( _parse_args_in_yunohost_format(answers, questions) == expected_result ) expected_result = OrderedDict({"some_user": (other_user, "user")}) - with patch.object(msignals, "prompt", return_value=other_user): + with patch.object(Moulinette.interface, "prompt", return_value=other_user): assert ( _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -1447,14 +1447,14 @@ def test_parse_args_in_yunohost_format_number_input(): answers = {} expected_result = OrderedDict({"some_number": (1337, "number")}) - with patch.object(msignals, "prompt", return_value="1337"): + with patch.object(Moulinette.interface, "prompt", return_value="1337"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result - with patch.object(msignals, "prompt", return_value=1337): + with patch.object(Moulinette.interface, "prompt", return_value=1337): assert _parse_args_in_yunohost_format(answers, questions) == expected_result expected_result = OrderedDict({"some_number": (0, "number")}) - with patch.object(msignals, "prompt", return_value=""): + with patch.object(Moulinette.interface, "prompt", return_value=""): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -1468,7 +1468,7 @@ def test_parse_args_in_yunohost_format_number_input_no_ask(): answers = {} expected_result = OrderedDict({"some_number": (1337, "number")}) - with patch.object(msignals, "prompt", return_value="1337"): + with patch.object(Moulinette.interface, "prompt", return_value="1337"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -1497,7 +1497,7 @@ def test_parse_args_in_yunohost_format_number_optional_with_input(): answers = {} expected_result = OrderedDict({"some_number": (1337, "number")}) - with patch.object(msignals, "prompt", return_value="1337"): + with patch.object(Moulinette.interface, "prompt", return_value="1337"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -1512,7 +1512,7 @@ def test_parse_args_in_yunohost_format_number_optional_with_input_without_ask(): answers = {} expected_result = OrderedDict({"some_number": (0, "number")}) - with patch.object(msignals, "prompt", return_value="0"): + with patch.object(Moulinette.interface, "prompt", return_value="0"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -1555,7 +1555,7 @@ def test_parse_args_in_yunohost_format_number_input_test_ask(): ] answers = {} - with patch.object(msignals, "prompt", return_value="1111") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="1111") as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with("%s (default: 0)" % (ask_text), False) @@ -1573,7 +1573,7 @@ def test_parse_args_in_yunohost_format_number_input_test_ask_with_default(): ] answers = {} - with patch.object(msignals, "prompt", return_value="1111") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="1111") as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with("%s (default: %s)" % (ask_text, default_value), False) @@ -1592,7 +1592,7 @@ def test_parse_args_in_yunohost_format_number_input_test_ask_with_example(): ] answers = {} - with patch.object(msignals, "prompt", return_value="1111") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="1111") as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert example_value in prompt.call_args[0][0] @@ -1612,7 +1612,7 @@ def test_parse_args_in_yunohost_format_number_input_test_ask_with_help(): ] answers = {} - with patch.object(msignals, "prompt", return_value="1111") as prompt: + with patch.object(Moulinette.interface, "prompt", return_value="1111") as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert help_value in prompt.call_args[0][0] From cd917a9123de55245f5bc39612c82430fd4e123a Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Sun, 22 Aug 2021 21:57:50 +0200 Subject: [PATCH 061/114] [fix] Wording in path question --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 693e9d24d..2cb3fc329 100644 --- a/locales/en.json +++ b/locales/en.json @@ -32,7 +32,7 @@ "app_location_unavailable": "This URL is either unavailable, or conflicts with the already installed app(s):\n{apps}", "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_path": "Choose the web path after the domain 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?", From b5d00da0bfbc9c5fa53c5b4e2820170ba2e46394 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 23 Aug 2021 14:05:22 +0200 Subject: [PATCH 062/114] Attempt to fix tests for ldap auth --- src/yunohost/tests/test_ldapauth.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/yunohost/tests/test_ldapauth.py b/src/yunohost/tests/test_ldapauth.py index 0ad8366c9..7560608f5 100644 --- a/src/yunohost/tests/test_ldapauth.py +++ b/src/yunohost/tests/test_ldapauth.py @@ -16,12 +16,12 @@ def setup_function(function): def test_authenticate(): - LDAPAuth().authenticate(credentials="yunohost") + LDAPAuth().authenticate_credentials(credentials="yunohost") def test_authenticate_with_wrong_password(): with pytest.raises(MoulinetteError) as exception: - LDAPAuth().authenticate(credentials="bad_password_lul") + LDAPAuth().authenticate_credentials(credentials="bad_password_lul") translation = m18n.g("invalid_password") expected_msg = translation.format() @@ -35,7 +35,7 @@ def test_authenticate_server_down(mocker): mocker.patch("os.system") mocker.patch("time.sleep") with pytest.raises(MoulinetteError) as exception: - LDAPAuth().authenticate(credentials="yunohost") + LDAPAuth().authenticate_credentials(credentials="yunohost") translation = m18n.n("ldap_server_down") expected_msg = translation.format() @@ -44,15 +44,15 @@ def test_authenticate_server_down(mocker): def test_authenticate_change_password(): - LDAPAuth().authenticate(credentials="yunohost") + LDAPAuth().authenticate_credentials(credentials="yunohost") tools_adminpw("plopette", check_strength=False) with pytest.raises(MoulinetteError) as exception: - LDAPAuth().authenticate(credentials="yunohost") + LDAPAuth().authenticate_credentials(credentials="yunohost") translation = m18n.g("invalid_password") expected_msg = translation.format() assert expected_msg in str(exception) - LDAPAuth().authenticate(credentials="plopette") + LDAPAuth().authenticate_credentials(credentials="plopette") From c9d73af4013816ccb5ba2eba47a3cb804b44d2ee Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 23 Aug 2021 15:31:43 +0200 Subject: [PATCH 063/114] i18n: mdns -> yunomdns --- locales/en.json | 2 +- locales/fr.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index cad1c8dcb..f2658c412 100644 --- a/locales/en.json +++ b/locales/en.json @@ -561,7 +561,7 @@ "service_already_started": "The service '{service}' is running already", "service_already_stopped": "The service '{service}' has already been stopped", "service_cmd_exec_failed": "Could not execute the command '{command}'", - "service_description_mdns": "Allows you to reach your server using 'yunohost.local' in your local network", + "service_description_yunomdns": "Allows you to reach your server using 'yunohost.local' in your local network", "service_description_dnsmasq": "Handles domain name resolution (DNS)", "service_description_dovecot": "Allows e-mail clients to access/fetch email (via IMAP and POP3)", "service_description_fail2ban": "Protects against brute-force and other kinds of attacks from the Internet", diff --git a/locales/fr.json b/locales/fr.json index 72afe80dd..d40b39c62 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -234,7 +234,7 @@ "migrations_list_conflict_pending_done": "Vous ne pouvez pas utiliser --previous et --done simultanément.", "migrations_to_be_ran_manually": "La migration {id} doit être lancée manuellement. Veuillez aller dans Outils > Migrations dans l’interface admin, ou lancer `yunohost tools migrations run`.", "migrations_need_to_accept_disclaimer": "Pour lancer la migration {id}, vous devez accepter cet avertissement :\n---\n{disclaimer}\n---\nSi vous acceptez de lancer la migration, veuillez relancer la commande avec l’option --accept-disclaimer.", - "service_description_mdns": "Vous permet d’atteindre votre serveur en utilisant « yunohost.local » sur votre réseau local", + "service_description_yunomdns": "Vous permet d’atteindre votre serveur en utilisant « yunohost.local » sur votre réseau local", "service_description_dnsmasq": "Gère la résolution des noms de domaine (DNS)", "service_description_dovecot": "Permet aux clients de messagerie d’accéder/récupérer les courriels (via IMAP et POP3)", "service_description_fail2ban": "Protège contre les attaques brute-force et autres types d’attaques venant d’Internet", From f4d0106c367902cad01e49808b570816f12fda12 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Mon, 23 Aug 2021 14:36:55 +0000 Subject: [PATCH 064/114] [CI] Remove stale translated strings --- locales/ar.json | 1 - locales/ca.json | 1 - locales/de.json | 3 +-- locales/eo.json | 1 - locales/es.json | 1 - locales/fr.json | 2 +- locales/gl.json | 2 +- locales/it.json | 1 - locales/oc.json | 1 - locales/pt.json | 2 +- locales/zh_Hans.json | 1 - 11 files changed, 4 insertions(+), 12 deletions(-) diff --git a/locales/ar.json b/locales/ar.json index 1d3dbd49d..3e5248917 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -83,7 +83,6 @@ "yunohost_installing": "عملية تنصيب يونوهوست جارية …", "yunohost_not_installed": "إنَّ واي يونوهوست ليس مُنَصَّب أو هو مثبت حاليا بشكل خاطئ. قم بتنفيذ الأمر 'yunohost tools postinstall'", "migrations_list_conflict_pending_done": "لا يمكنك استخدام --previous و --done معًا على نفس سطر الأوامر.", - "service_description_mdns": "يسمح لك بالنفاذ إلى خادومك عبر الشبكة المحلية باستخدام yunohost.local", "service_description_metronome": "يُدير حسابات الدردشة الفورية XMPP", "service_description_nginx": "يقوم بتوفير النفاذ و السماح بالوصول إلى كافة مواقع الويب المستضافة على خادومك", "service_description_postfix": "يقوم بإرسال و تلقي الرسائل البريدية الإلكترونية", diff --git a/locales/ca.json b/locales/ca.json index bfdb57c9e..d01c0da0b 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -283,7 +283,6 @@ "service_already_started": "El servei «{service}» ja està funcionant", "service_already_stopped": "Ja s'ha aturat el servei «{service}»", "service_cmd_exec_failed": "No s'ha pogut executar l'ordre «{command}»", - "service_description_mdns": "Permet accedir al servidor via «yunohost.local» en la xarxa local", "service_description_dnsmasq": "Gestiona la resolució del nom de domini (DNS)", "service_description_dovecot": "Permet als clients de correu accedir/recuperar correus (via IMAP i POP3)", "service_description_fail2ban": "Protegeix contra els atacs de força bruta i a altres atacs provinents d'Internet", diff --git a/locales/de.json b/locales/de.json index ef8593bd6..ea20c7d7f 100644 --- a/locales/de.json +++ b/locales/de.json @@ -597,7 +597,6 @@ "service_description_fail2ban": "Schützt gegen Brute-Force-Angriffe und andere Angriffe aus dem Internet", "service_description_dovecot": "Ermöglicht es E-Mail-Clients auf Konten zuzugreifen (IMAP und POP3)", "service_description_dnsmasq": "Verarbeitet die Auflösung des Domainnamens (DNS)", - "service_description_mdns": "Erlaubt, den Server im lokalen Netz über 'yunohost.local' zu erreichen", "restore_backup_too_old": "Dieses Backup kann nicht wieder hergestellt werden, weil es von einer zu alten YunoHost Version stammt.", "service_description_slapd": "Speichert Benutzer, Domains und verbundene Informationen", "service_description_rspamd": "Spamfilter und andere E-Mail-Merkmale", @@ -636,4 +635,4 @@ "global_settings_setting_security_webadmin_allowlist_enabled": "Erlaube nur bestimmten IP-Adressen den Zugriff auf die Verwaltungsseite.", "disk_space_not_sufficient_update": "Es ist nicht genügend Speicherplatz frei, um diese Applikation zu aktuallisieren", "disk_space_not_sufficient_install": "Es ist nicht genügend Speicherplatz frei, um diese Applikation zu installieren" -} +} \ No newline at end of file diff --git a/locales/eo.json b/locales/eo.json index 8fac2e50e..76cec1264 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -332,7 +332,6 @@ "hook_exec_failed": "Ne povis funkcii skripto: {path}", "global_settings_cant_open_settings": "Ne eblis malfermi agordojn, tial: {reason}", "user_created": "Uzanto kreita", - "service_description_mdns": "Permesas al vi atingi vian servilon uzante 'yunohost.local' en via loka reto", "certmanager_attempt_to_replace_valid_cert": "Vi provas anstataŭigi bonan kaj validan atestilon por domajno {domain}! (Uzu --forte pretervidi)", "regenconf_updated": "Agordo ĝisdatigita por '{category}'", "update_apt_cache_warning": "Io iris malbone dum la ĝisdatigo de la kaŝmemoro de APT (paka administranto de Debian). Jen rubujo de la sources.list-linioj, kiuj povus helpi identigi problemajn liniojn:\n{sourceslist}", diff --git a/locales/es.json b/locales/es.json index 44d637796..560bfe240 100644 --- a/locales/es.json +++ b/locales/es.json @@ -238,7 +238,6 @@ "service_description_fail2ban": "Protege contra ataques de fuerza bruta y otras clases de ataques desde Internet", "service_description_dovecot": "Permite a los clientes de correo acceder/obtener correo (vía IMAP y POP3)", "service_description_dnsmasq": "Maneja la resolución de nombres de dominio (DNS)", - "service_description_mdns": "Permite acceder a su servidor usando «yunohost.local» en su red local", "server_reboot_confirm": "El servidor se reiniciará inmediatamente ¿está seguro? [{answers}]", "server_reboot": "El servidor se reiniciará", "server_shutdown_confirm": "El servidor se apagará inmediatamente ¿está seguro? [{answers}]", diff --git a/locales/fr.json b/locales/fr.json index d40b39c62..f1ccde84d 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -636,4 +636,4 @@ "backup_create_size_estimation": "L'archive contiendra environ {size} de données.", "global_settings_setting_security_webadmin_allowlist": "Adresses IP autorisées à accéder à la page web du portail d'administration (webadmin). Elles doivent être séparées par une virgule.", "global_settings_setting_security_webadmin_allowlist_enabled": "Autorisez seulement certaines IP à accéder à la page web du portail d'administration (webadmin)." -} +} \ No newline at end of file diff --git a/locales/gl.json b/locales/gl.json index 6ef2c45c1..a9d901275 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -358,4 +358,4 @@ "global_settings_setting_security_webadmin_allowlist_enabled": "Permitir que só algúns IPs accedan á webadmin.", "disk_space_not_sufficient_update": "Non hai espazo suficiente no disco para actualizar esta aplicación", "disk_space_not_sufficient_install": "Non queda espazo suficiente no disco para instalar esta aplicación" -} +} \ No newline at end of file diff --git a/locales/it.json b/locales/it.json index c818dd14c..543ab99bd 100644 --- a/locales/it.json +++ b/locales/it.json @@ -428,7 +428,6 @@ "service_description_fail2ban": "Ti protegge dal brute-force e altri tipi di attacchi da Internet", "service_description_dovecot": "Consente ai client mail di accedere/recuperare le email (via IMAP e POP3)", "service_description_dnsmasq": "Gestisce la risoluzione dei domini (DNS)", - "service_description_mdns": "Consente di raggiungere il tuo server eseguendo 'yunohost.local' sulla tua LAN", "server_reboot_confirm": "Il server si riavvierà immediatamente, sei sicuro? [{answers}]", "server_reboot": "Il server si riavvierà", "server_shutdown_confirm": "Il server si spegnerà immediatamente, sei sicuro? [{answers}]", diff --git a/locales/oc.json b/locales/oc.json index fb0a7006d..906f67106 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -193,7 +193,6 @@ "user_unknown": "Utilizaire « {user} » desconegut", "user_update_failed": "Modificacion impossibla de l’utilizaire", "user_updated": "L’utilizaire es estat modificat", - "service_description_mdns": "permet d’aténher vòstre servidor via yunohost.local sus vòstre ret local", "service_description_dnsmasq": "gerís la resolucion dels noms de domeni (DNS)", "updating_apt_cache": "Actualizacion de la lista dels paquets disponibles…", "server_reboot_confirm": "Lo servidor es per reaviar sul pic, o volètz vertadièrament ? {answers}", diff --git a/locales/pt.json b/locales/pt.json index 82edbf349..353d48744 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -137,4 +137,4 @@ "already_up_to_date": "Nada a ser feito. Tudo já está atualizado.", "additional_urls_already_removed": "A URL adicional '{url}'já está removida para a permissão '{permission}'", "additional_urls_already_added": "A URL adicional '{url}' já está adicionada para a permissão '{permission}'" -} +} \ No newline at end of file diff --git a/locales/zh_Hans.json b/locales/zh_Hans.json index 7951961b6..5f076fa2e 100644 --- a/locales/zh_Hans.json +++ b/locales/zh_Hans.json @@ -226,7 +226,6 @@ "service_description_fail2ban": "防止来自互联网的暴力攻击和其他类型的攻击", "service_description_dovecot": "允许电子邮件客户端访问/获取电子邮件(通过IMAP和POP3)", "service_description_dnsmasq": "处理域名解析(DNS)", - "service_description_mdns": "允许您使用本地网络中的“ yunohost.local”访问服务器", "service_started": "服务 '{service}' 已启动", "service_start_failed": "无法启动服务 '{service}'\n\n最近的服务日志:{logs}", "service_reloaded_or_restarted": "服务'{service}'已重新加载或重新启动", From 07cd1428d24fec44f3c6ca0d516f19d28d107f97 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 26 Aug 2021 20:06:39 +0200 Subject: [PATCH 065/114] Update locales/en.json --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 2cb3fc329..109c4b16b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -32,7 +32,7 @@ "app_location_unavailable": "This URL is either unavailable, or conflicts with the already installed app(s):\n{apps}", "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 web path after the domain where this app should be installed", + "app_manifest_install_ask_path": "Choose the url path (after the domain) 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?", From aac0146aef754315764705a8f45084ff0f9209e1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 26 Aug 2021 20:14:23 +0200 Subject: [PATCH 066/114] Forbid installing apps with a dot in app id --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 57498c644..490368a15 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -887,7 +887,7 @@ def app_install( raise YunohostValidationError("disk_space_not_sufficient_install") # Check ID - if "id" not in manifest or "__" in manifest["id"]: + if "id" not in manifest or "__" in manifest["id"] or "." in manifest["id"]: raise YunohostValidationError("app_id_invalid") app_id = manifest["id"] From 4f0494d66bb78974b3f8e25522f8ad74475b95d8 Mon Sep 17 00:00:00 2001 From: ericgaspar Date: Thu, 26 Aug 2021 20:57:23 +0200 Subject: [PATCH 067/114] Update en.json --- locales/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index 27bdd63ab..83c270b58 100644 --- a/locales/en.json +++ b/locales/en.json @@ -32,7 +32,7 @@ "app_location_unavailable": "This URL is either unavailable, or conflicts with the already installed app(s):\n{apps}", "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 url path (after the domain) where this app should be installed", + "app_manifest_install_ask_path": "Choose the URL path (after the domain) 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?", @@ -399,7 +399,7 @@ "log_letsencrypt_cert_install": "Install a Let's Encrypt certificate on '{}' domain", "log_permission_create": "Create permission '{}'", "log_permission_delete": "Delete permission '{}'", - "log_permission_url": "Update url related to permission '{}'", + "log_permission_url": "Update URL related to permission '{}'", "log_selfsigned_cert_install": "Install self-signed certificate on '{}' domain", "log_letsencrypt_cert_renew": "Renew '{}' Let's Encrypt certificate", "log_regen_conf": "Regenerate system configurations '{}'", From aac8b8d4608db5ab57a89beed6118996c7b27b63 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Thu, 26 Aug 2021 20:09:57 +0000 Subject: [PATCH 068/114] [CI] Format code --- data/hooks/diagnosis/12-dnsrecords.py | 10 +++++++--- data/hooks/diagnosis/21-web.py | 2 +- src/yunohost/domain.py | 4 +++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index 1db4af685..6110024f4 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -30,9 +30,14 @@ class DNSRecordsDiagnoser(Diagnoser): for domain in all_domains: self.logger_debug("Diagnosing DNS conf for %s" % domain) is_subdomain = domain.split(".", 1)[1] in all_domains - is_specialusedomain = any(domain.endswith("." + tld) for tld in SPECIAL_USE_TLDS) + is_specialusedomain = any( + domain.endswith("." + tld) for tld in SPECIAL_USE_TLDS + ) for report in self.check_domain( - domain, domain == main_domain, is_subdomain=is_subdomain, is_specialusedomain=is_specialusedomain + domain, + domain == main_domain, + is_subdomain=is_subdomain, + is_specialusedomain=is_specialusedomain, ): yield report @@ -70,7 +75,6 @@ class DNSRecordsDiagnoser(Diagnoser): summary="diagnosis_dns_specialusedomain", ) - for category in categories: records = expected_configuration[category] diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index 04c36661e..40a6c26b4 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -34,7 +34,7 @@ class WebDiagnoser(Diagnoser): summary="diagnosis_http_nginx_conf_not_up_to_date", details=["diagnosis_http_nginx_conf_not_up_to_date_details"], ) - elif domain.endswith('.local'): + elif domain.endswith(".local"): yield dict( meta={"domain": domain}, status="INFO", diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 3c9192b8f..09d419c71 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -166,7 +166,9 @@ def domain_add(operation_logger, domain, dyndns=False): # because it's one of the major service, but in the long term we # should identify the root of this bug... _force_clear_hashes(["/etc/nginx/conf.d/%s.conf" % domain]) - regen_conf(names=["nginx", "metronome", "dnsmasq", "postfix", "rspamd", "mdns"]) + regen_conf( + names=["nginx", "metronome", "dnsmasq", "postfix", "rspamd", "mdns"] + ) app_ssowatconf() except Exception as e: From 2435f84871a82e1eed43b810eeecacee488bcafd Mon Sep 17 00:00:00 2001 From: Flavio Cristoforetti Date: Wed, 18 Aug 2021 14:21:25 +0000 Subject: [PATCH 069/114] Translated using Weblate (Italian) Currently translated at 100.0% (637 of 637 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/it/ --- locales/it.json | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/locales/it.json b/locales/it.json index c818dd14c..9cffa03d8 100644 --- a/locales/it.json +++ b/locales/it.json @@ -30,7 +30,7 @@ "app_not_correctly_installed": "{app} sembra di non essere installata correttamente", "app_not_properly_removed": "{app} non è stata correttamente rimossa", "action_invalid": "L'azione '{action}' non è valida", - "app_removed": "{app} rimossa", + "app_removed": "{app} disinstallata", "app_sources_fetch_failed": "Impossibile riportare i file sorgenti, l'URL è corretto?", "app_upgrade_failed": "Impossibile aggiornare {app}: {error}", "app_upgraded": "{app} aggiornata", @@ -172,7 +172,7 @@ "backup_applying_method_custom": "Chiamando il metodo di backup personalizzato '{method}'...", "backup_applying_method_tar": "Creando l'archivio TAR del backup...", "backup_archive_system_part_not_available": "La parte di sistema '{part}' non è disponibile in questo backup", - "backup_archive_writing_error": "Impossibile aggiungere i file '{source}' (indicati nell'archivio '{dest}') al backup nell'archivio compresso '{archive}'", + "backup_archive_writing_error": "Impossibile aggiungere i file '{source}' (indicati nell'archivio '{dest}') al backup nell'archivio compresso '{archive}'", "backup_ask_for_copying_if_needed": "Vuoi effettuare il backup usando {size}MB temporaneamente? (È necessario usare questo sistema poiché alcuni file non possono essere preparati in un modo più efficiente)", "backup_cant_mount_uncompress_archive": "Impossibile montare in modalità sola lettura la cartella di archivio non compressa", "backup_copying_to_organize_the_archive": "Copiando {size}MB per organizzare l'archivio", @@ -631,5 +631,9 @@ "diagnosis_sshd_config_inconsistent": "Sembra che la porta SSH sia stata modificata manualmente in /etc/ssh/sshd_config: A partire da YunoHost 4.2, una nuova configurazione globale 'security.ssh.port' è disponibile per evitare di modificare manualmente la configurazione.", "diagnosis_sshd_config_insecure": "Sembra che la configurazione SSH sia stata modificata manualmente, ed non è sicuro dato che non contiene le direttive 'AllowGroups' o 'Allowusers' che limitano l'accesso agli utenti autorizzati.", "backup_create_size_estimation": "L'archivio conterrà circa {size} di dati.", - "app_restore_script_failed": "C'è stato un errore all'interno dello script di recupero" -} \ No newline at end of file + "app_restore_script_failed": "C'è stato un errore all'interno dello script di recupero", + "global_settings_setting_security_webadmin_allowlist": "Indirizzi IP con il permesso di accedere al webadmin, separati da virgola.", + "global_settings_setting_security_webadmin_allowlist_enabled": "Permetti solo ad alcuni IP di accedere al webadmin.", + "disk_space_not_sufficient_update": "Non c'è abbastanza spazio libero per aggiornare questa applicazione", + "disk_space_not_sufficient_install": "Non c'è abbastanza spazio libero per installare questa applicazione" +} From cb36c781dcca69f0be16ee7533aeea5f6137413f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M?= Date: Wed, 18 Aug 2021 06:04:28 +0000 Subject: [PATCH 070/114] Translated using Weblate (Galician) Currently translated at 58.5% (373 of 637 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/gl/ --- locales/gl.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/locales/gl.json b/locales/gl.json index 6ef2c45c1..cc08bf022 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -357,5 +357,20 @@ "global_settings_setting_security_webadmin_allowlist": "Enderezos IP con permiso para acceder á webadmin. Separados por vírgulas.", "global_settings_setting_security_webadmin_allowlist_enabled": "Permitir que só algúns IPs accedan á webadmin.", "disk_space_not_sufficient_update": "Non hai espazo suficiente no disco para actualizar esta aplicación", - "disk_space_not_sufficient_install": "Non queda espazo suficiente no disco para instalar esta aplicación" + "disk_space_not_sufficient_install": "Non queda espazo suficiente no disco para instalar esta aplicación", + "log_help_to_get_log": "Para ver o rexistro completo da operación '{desc}', usa o comando 'yunohost log show {name}{name}'", + "log_link_to_log": "Rexistro completo desta operación: '{desc}'", + "log_corrupted_md_file": "O ficheiro YAML con metadatos asociado aos rexistros está danado: '{md_file}\nErro: {error}'", + "iptables_unavailable": "Non podes andar remexendo en iptables aquí. Ou ben estás nun contedor ou o teu kernel non ten soporte para isto", + "ip6tables_unavailable": "Non podes remexer en ip6tables aquí. Ou ben estás nun contedor ou o teu kernel non ten soporte para isto", + "invalid_regex": "Regex non válido: '{regex}'", + "installation_complete": "Instalación completa", + "hook_name_unknown": "Nome descoñecido do gancho '{name}'", + "hook_list_by_invalid": "Esta propiedade non se pode usar para enumerar os ganchos", + "hook_json_return_error": "Non se puido ler a info de retorno do gancho {path}. Erro: {msg}. Contido en bruto: {raw_content}", + "hook_exec_not_terminated": "O script non rematou correctamente: {path}", + "hook_exec_failed": "Non se executou o script: {path}", + "group_user_not_in_group": "A usuaria {user} non está no grupo {group}", + "group_user_already_in_group": "A usuaria {user} xa está no grupo {group}", + "group_update_failed": "Non se actualizou o grupo '{group}': {error}" } From 1c0b9368ae98cd5622c8f9419a028f8411e9ea0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Gaspar?= Date: Fri, 20 Aug 2021 06:05:11 +0000 Subject: [PATCH 071/114] Translated using Weblate (French) Currently translated at 100.0% (637 of 637 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index d40b39c62..c9fddab57 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -571,7 +571,7 @@ "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 yunohost dyndns update --force.", "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}", - "global_settings_setting_backup_compress_tar_archives": "Compresser les archives (.tar.gz) au lieu de créer des archives non-compressées lors de la création des backups. N.B. Activer cette option permet de créer des archives plus légères, mais aussi d'avoir des procédures de backup significativement plus longues et plus gourmandes en CPU.", + "global_settings_setting_backup_compress_tar_archives": "Compresser les archives (.tar.gz) au lieu de créer des archives non-compressées lors de la création des backups. N.B. Activer cette option implique de créer des archives plus légères, mais aussi d'avoir des procédures de backup significativement plus longues et plus gourmandes en CPU.", "migration_description_0018_xtable_to_nftable": "Migrer les anciennes règles de trafic réseau vers le nouveau système basé sur nftables", "service_description_php7.3-fpm": "Exécute les applications écrites en PHP avec NGINX", "migration_0018_failed_to_reset_legacy_rules": "La réinitialisation des règles iptable par défaut a échoué : {error}", From 64889b9618049a6617e6ea573198ae53f3291140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M?= Date: Fri, 20 Aug 2021 05:19:32 +0000 Subject: [PATCH 072/114] Translated using Weblate (Galician) Currently translated at 62.4% (398 of 637 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/gl/ --- locales/gl.json | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/locales/gl.json b/locales/gl.json index cc08bf022..45e8ffafe 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -372,5 +372,30 @@ "hook_exec_failed": "Non se executou o script: {path}", "group_user_not_in_group": "A usuaria {user} non está no grupo {group}", "group_user_already_in_group": "A usuaria {user} xa está no grupo {group}", - "group_update_failed": "Non se actualizou o grupo '{group}': {error}" + "group_update_failed": "Non se actualizou o grupo '{group}': {error}", + "log_permission_delete": "Eliminar permiso '{}'", + "log_permission_create": "Crear permiso '{}'", + "log_letsencrypt_cert_install": "Instalar un certificado Let's Encrypt para o dominio '{}'", + "log_dyndns_update": "Actualizar o IP asociado ao teu subdominio YunoHost '{}'", + "log_dyndns_subscribe": "Subscribirse a un subdominio YunoHost '{}'", + "log_domain_remove": "Eliminar o dominio '{}' da configuración do sistema", + "log_domain_add": "Engadir dominio '{}' á configuración do sistema", + "log_remove_on_failed_install": "Eliminar '{}' tras unha instalación fallida", + "log_remove_on_failed_restore": "Eliminar '{}' tras un intento fallido de restablecemento desde copia", + "log_backup_restore_app": "Restablecer '{}' desde unha copia de apoio", + "log_backup_restore_system": "Restablecer o sistema desde unha copia de apoio", + "log_backup_create": "Crear copia de apoio", + "log_available_on_yunopaste": "Este rexistro está dispoñible en {url}", + "log_app_config_apply": "Aplicar a configuración da app '{}'", + "log_app_config_show_panel": "Mostrar o panel de configuración da app '{}'", + "log_app_action_run": "Executar acción da app '{}'", + "log_app_makedefault": "Converter '{}' na app por defecto", + "log_app_upgrade": "Actualizar a app '{}'", + "log_app_remove": "Eliminar a app '{}'", + "log_app_install": "Instalar a app '{}'", + "log_app_change_url": "Cambiar o URL da app '{}'", + "log_operation_unit_unclosed_properly": "Non se pechou correctamente a unidade da operación", + "log_does_exists": "Non hai rexistro de operación co nome '{log}', usa 'yunohost log list' para ver tódolos rexistros de operacións dispoñibles", + "log_help_to_get_failed_log": "A operación '{desc}' non se completou. Comparte o rexistro completo da operación utilizando o comando 'yunohost log share {name}' para obter axuda", + "log_link_to_failed_log": "Non se completou a operación '{desc}'. Por favor envía o rexistro completo desta operación premendo aquí para obter axuda" } From 3e84789a90c34b0569bfc5c4ededa7afe841da6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M?= Date: Sat, 21 Aug 2021 05:14:06 +0000 Subject: [PATCH 073/114] Translated using Weblate (Galician) Currently translated at 68.6% (437 of 637 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/gl/ --- locales/gl.json | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/locales/gl.json b/locales/gl.json index 45e8ffafe..c2b6b0161 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -397,5 +397,44 @@ "log_operation_unit_unclosed_properly": "Non se pechou correctamente a unidade da operación", "log_does_exists": "Non hai rexistro de operación co nome '{log}', usa 'yunohost log list' para ver tódolos rexistros de operacións dispoñibles", "log_help_to_get_failed_log": "A operación '{desc}' non se completou. Comparte o rexistro completo da operación utilizando o comando 'yunohost log share {name}' para obter axuda", - "log_link_to_failed_log": "Non se completou a operación '{desc}'. Por favor envía o rexistro completo desta operación premendo aquí para obter axuda" + "log_link_to_failed_log": "Non se completou a operación '{desc}'. Por favor envía o rexistro completo desta operación premendo aquí para obter axuda", + "migration_0015_start": "Comezando a migración a Buster", + "migration_update_LDAP_schema": "Actualizando esquema LDAP...", + "migration_ldap_rollback_success": "Sistema restablecido.", + "migration_ldap_migration_failed_trying_to_rollback": "Non se puido migrar... intentando volver á versión anterior do sistema.", + "migration_ldap_can_not_backup_before_migration": "O sistema de copia de apoio do sistema non se completou antes de que fallase a migración. Erro: {error}", + "migration_ldap_backup_before_migration": "Crear copia de apoio da base de datos LDAP e axustes de apps antes de realizar a migración.", + "migration_description_0020_ssh_sftp_permissions": "Engadir soporte para permisos SSH e SFTP", + "migration_description_0019_extend_permissions_features": "Extender/recrear o sistema de xestión de permisos de apps", + "migration_description_0018_xtable_to_nftable": "Migrar as regras de tráfico de rede antigas ao novo sistema nftable", + "migration_description_0017_postgresql_9p6_to_11": "Migrar bases de datos desde PostgreSQL 9.6 a 11", + "migration_description_0016_php70_to_php73_pools": "Migrar o ficheiros de configuración 'pool' de php7.0-fpm a php7.3", + "migration_description_0015_migrate_to_buster": "Actualizar o sistema a Debian Buster e YunoHost 4.x", + "migrating_legacy_permission_settings": "Migrando os axustes dos permisos anteriores...", + "main_domain_changed": "Foi cambiado o dominio principal", + "main_domain_change_failed": "Non se pode cambiar o dominio principal", + "mail_unavailable": "Este enderezo de email está reservado e debería adxudicarse automáticamente á primeira usuaria", + "mailbox_used_space_dovecot_down": "O servizo de caixa de correo Dovecot ten que estar activo se queres obter o espazo utilizado polo correo", + "mailbox_disabled": "Desactivado email para usuaria {user}", + "mail_forward_remove_failed": "Non se eliminou o reenvío de email '{mail}'", + "mail_domain_unknown": "Enderezo de email non válido para o dominio '{domain}'. Usa un dominio administrado por este servidor.", + "mail_alias_remove_failed": "Non se puido eliminar o alias de email '{mail}'", + "log_tools_reboot": "Reiniciar o servidor", + "log_tools_shutdown": "Apagar o servidor", + "log_tools_upgrade": "Actualizar paquetes do sistema", + "log_tools_postinstall": "Postinstalación do servidor YunoHost", + "log_tools_migrations_migrate_forward": "Executar migracións", + "log_domain_main_domain": "Facer que '{}' sexa o dominio principal", + "log_user_permission_reset": "Restablecer permiso '{}'", + "log_user_permission_update": "Actualizar accesos para permiso '{}'", + "log_user_update": "Actualizar info da usuaria '{}'", + "log_user_group_update": "Actualizar grupo '{}'", + "log_user_group_delete": "Eliminar grupo '{}'", + "log_user_group_create": "Crear grupo '{}'", + "log_user_delete": "Eliminar usuaria '{}'", + "log_user_create": "Engadir usuaria '{}'", + "log_regen_conf": "Rexerar configuración do sistema '{}'", + "log_letsencrypt_cert_renew": "Anovar certificado Let's Encrypt para '{}'", + "log_selfsigned_cert_install": "Instalar certificado auto-asinado para o dominio '{}'", + "log_permission_url": "Actualizar url relativo ao permiso '{}'" } From 23b8782516d565a5e830c0a56a8510ba124ef44e Mon Sep 17 00:00:00 2001 From: mifegui Date: Sun, 22 Aug 2021 12:07:23 +0000 Subject: [PATCH 074/114] Translated using Weblate (Portuguese) Currently translated at 9.2% (59 of 637 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/pt/ --- locales/pt.json | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/locales/pt.json b/locales/pt.json index 82edbf349..ef0c41349 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -4,9 +4,9 @@ "admin_password_change_failed": "Não foi possível alterar a senha", "admin_password_changed": "A senha da administração foi alterada", "app_already_installed": "{app} já está instalada", - "app_extraction_failed": "Não foi possível extrair os ficheiros para instalação", - "app_id_invalid": "A ID da aplicação é inválida", - "app_install_files_invalid": "Ficheiros para instalação corrompidos", + "app_extraction_failed": "Não foi possível extrair os arquivos para instalação", + "app_id_invalid": "App ID invaĺido", + "app_install_files_invalid": "Esses arquivos não podem ser instalados", "app_manifest_invalid": "Manifesto da aplicação inválido: {error}", "app_not_installed": "{app} não está instalada", "app_removed": "{app} removida com êxito", @@ -136,5 +136,9 @@ "app_action_broke_system": "Esta ação parece ter quebrado estes serviços importantes: {services}", "already_up_to_date": "Nada a ser feito. Tudo já está atualizado.", "additional_urls_already_removed": "A URL adicional '{url}'já está removida para a permissão '{permission}'", - "additional_urls_already_added": "A URL adicional '{url}' já está adicionada para a permissão '{permission}'" + "additional_urls_already_added": "A URL adicional '{url}' já está adicionada para a permissão '{permission}'", + "app_install_script_failed": "Ocorreu um erro dentro do script de instalação do aplicativo", + "app_install_failed": "Não foi possível instalar {app}: {error}", + "app_full_domain_unavailable": "Desculpe, esse app deve ser instalado num domínio próprio mas já há outros apps instalados no domínio '{domain}'. Você pode usar um subdomínio dedicado a esse aplicativo.", + "app_change_url_success": "A URL agora é {domain}{path}" } From 97f73458d703b5f2bc4f9dc4026551fd73e9b653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M?= Date: Mon, 23 Aug 2021 12:11:11 +0000 Subject: [PATCH 075/114] Translated using Weblate (Galician) Currently translated at 69.8% (445 of 637 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/gl/ --- locales/gl.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/locales/gl.json b/locales/gl.json index c2b6b0161..42ded4733 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -436,5 +436,13 @@ "log_regen_conf": "Rexerar configuración do sistema '{}'", "log_letsencrypt_cert_renew": "Anovar certificado Let's Encrypt para '{}'", "log_selfsigned_cert_install": "Instalar certificado auto-asinado para o dominio '{}'", - "log_permission_url": "Actualizar url relativo ao permiso '{}'" + "log_permission_url": "Actualizar url relativo ao permiso '{}'", + "migration_0015_general_warning": "Ten en conta que a migración é unha operación delicada. O equipo YunoHost esforzouse revisando e comprobandoa, aínda así algo podería fallar en partes do teu sistema ou as súas apps.\n\nPor tanto, é recomendable:\n- realiza unha copia de apoio de tódolos datos ou apps importantes. Máis info en https://yunohost.org/backup;\n - ten paciencia tras iniciar o proceso: dependendo da túa conexión de internet e hardware podería demorar varias horas a actualización de tódolos compoñentes.", + "migration_0015_system_not_fully_up_to_date": "O teu sistema non está ao día. Realiza unha actualización común antes de realizar a migración a Buster.", + "migration_0015_not_enough_free_space": "Queda moi pouco espazo en /var/! Deberías ter polo menos 1GB libre para realizar a migración.", + "migration_0015_not_stretch": "A distribución Debian actual non é Stretch!", + "migration_0015_yunohost_upgrade": "Iniciando a actualización do núcleo YunoHost...", + "migration_0015_still_on_stretch_after_main_upgrade": "Algo foi mal durante a actualiza ión principal, o sistema semella que aínda está en Debian Stretch", + "migration_0015_main_upgrade": "Iniciando a actualización principal...", + "migration_0015_patching_sources_list": "Correxindo os sources.lists..." } From f50bf89609eda035ee0deede54fb1674cd02a3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Gaspar?= Date: Mon, 23 Aug 2021 17:09:20 +0000 Subject: [PATCH 076/114] Translated using Weblate (French) Currently translated at 100.0% (639 of 639 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index c9fddab57..4ac519c11 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -285,7 +285,7 @@ "mail_unavailable": "Cette adresse de courriel est réservée et doit être automatiquement attribuée au tout premier utilisateur", "good_practices_about_admin_password": "Vous êtes sur le point de définir un nouveau mot de passe d'administration. Le mot de passe doit comporter au moins 8 caractères, bien qu'il soit recommandé d'utiliser un mot de passe plus long (c'est-à-dire une phrase secrète) et/ou d'utiliser une combinaison de caractères (majuscules, minuscules, chiffres et caractères spéciaux).", "good_practices_about_user_password": "Vous êtes sur le point de définir un nouveau mot de passe utilisateur. Le mot de passe doit comporter au moins 8 caractères, bien qu'il soit recommandé d'utiliser un mot de passe plus long (c'est-à-dire une phrase secrète) et/ou une combinaison de caractères (majuscules, minuscules, chiffres et caractères spéciaux).", - "password_listed": "Ce mot de passe fait partie des mots de passe les plus utilisés dans le monde. Veuillez en choisir un autre moins commun et plus fort.", + "password_listed": "Ce mot de passe fait partie des mots de passe les plus utilisés dans le monde. Veuillez en choisir un autre moins commun et plus robuste.", "password_too_simple_1": "Le mot de passe doit comporter au moins 8 caractères", "password_too_simple_2": "Le mot de passe doit comporter au moins 8 caractères et contenir des chiffres, des majuscules et des minuscules", "password_too_simple_3": "Le mot de passe doit comporter au moins 8 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux", @@ -635,5 +635,7 @@ "diagnosis_sshd_config_insecure": "La configuration SSH semble avoir été modifiée manuellement et n'est pas sécurisée car elle ne contient aucune directive 'AllowGroups' ou 'AllowUsers' pour limiter l'accès aux utilisateurs autorisés.", "backup_create_size_estimation": "L'archive contiendra environ {size} de données.", "global_settings_setting_security_webadmin_allowlist": "Adresses IP autorisées à accéder à la page web du portail d'administration (webadmin). Elles doivent être séparées par une virgule.", - "global_settings_setting_security_webadmin_allowlist_enabled": "Autorisez seulement certaines IP à accéder à la page web du portail d'administration (webadmin)." + "global_settings_setting_security_webadmin_allowlist_enabled": "Autorisez seulement certaines IP à accéder à la page web du portail d'administration (webadmin).", + "diagnosis_http_localdomain": "Le domaine {domain}, avec un TLD .local, ne devrait pas être atteint depuis l'extérieur du réseau local.", + "diagnosis_dns_specialusedomain": "Le domaine {domain} est basé sur un domaine de premier niveau (TLD) à usage spécial et ne devrait donc pas avoir d'enregistrements DNS réels." } From 17d9dd18c2f8b469c9f2bd963fb1d67c4034609b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M?= Date: Tue, 24 Aug 2021 04:11:15 +0000 Subject: [PATCH 077/114] Translated using Weblate (Galician) Currently translated at 71.9% (460 of 639 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/gl/ --- locales/gl.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/locales/gl.json b/locales/gl.json index 42ded4733..62fb10c13 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -444,5 +444,20 @@ "migration_0015_yunohost_upgrade": "Iniciando a actualización do núcleo YunoHost...", "migration_0015_still_on_stretch_after_main_upgrade": "Algo foi mal durante a actualiza ión principal, o sistema semella que aínda está en Debian Stretch", "migration_0015_main_upgrade": "Iniciando a actualización principal...", - "migration_0015_patching_sources_list": "Correxindo os sources.lists..." + "migration_0015_patching_sources_list": "Correxindo os sources.lists...", + "migrations_already_ran": "Xa se realizaron estas migracións: {ids}", + "migration_0019_slapd_config_will_be_overwritten": "Semella que editaches manualmente a configuración slapd. Para esta migración crítica YunoHost precisa forzar a actualización da configuración slapd. Os ficheiros orixinais van ser copiados en {conf_backup_folder}.", + "migration_0019_add_new_attributes_in_ldap": "Engadir novos atributos para os permisos na base de datos LDAP", + "migration_0018_failed_to_reset_legacy_rules": "Fallou o restablecemento das regras antigas de iptables: {error}", + "migration_0018_failed_to_migrate_iptables_rules": "Fallou a migración das regras antigas de iptables a nftables: {erro}", + "migration_0017_not_enough_space": "Crea espazo suficiente en {path} para executar a migración.", + "migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 está instado, pero non postgresql 11? Algo raro debeu acontecer no teu sistema :(...", + "migration_0017_postgresql_96_not_installed": "PostgreSQL non está instalado no teu sistema. Nada que facer.", + "migration_0015_weak_certs": "Os seguintes certificados están a utilizar algoritmos de sinatura débiles e teñen que ser actualizados para ser compatibles coa seguinte versión de nginx: {certs}", + "migration_0015_cleaning_up": "Limpando a caché e paquetes que xa non son útiles...", + "migration_0015_specific_upgrade": "Iniciando a actualización dos paquetes do sistema que precisan ser actualizados de xeito independente...", + "migration_0015_modified_files": "Ten en conta que os seguintes ficheiros semella que foron modificados manualmente e poderían ser sobrescritos na actualización: {manually_modified_files}", + "migration_0015_problematic_apps_warning": "Ten en conta que se detectaron as seguintes apps que poderían ser problemáticas. Semella que non foron instaladas usando o catálogo de YunoHost, ou non están marcadas como 'funcionais'. En consecuencia, non se pode garantir que seguirán funcionando após a actualización: {problematic_apps}", + "diagnosis_http_localdomain": "O dominio {domain}, cun TLD .local, non é de agardar que sexa accesible desde o exterior da rede local.", + "diagnosis_dns_specialusedomain": "O dominio {domain} baséase un dominio de nivel alto e uso especial (TLD) polo que non é de agardar que realmente teña rexistros DNS." } From bb6140af6c227ed930ee9f88dcb0af21fc6178f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M?= Date: Wed, 25 Aug 2021 04:25:57 +0000 Subject: [PATCH 078/114] Translated using Weblate (Galician) Currently translated at 79.1% (506 of 639 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/gl/ --- locales/gl.json | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/locales/gl.json b/locales/gl.json index 62fb10c13..46665b870 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -459,5 +459,51 @@ "migration_0015_modified_files": "Ten en conta que os seguintes ficheiros semella que foron modificados manualmente e poderían ser sobrescritos na actualización: {manually_modified_files}", "migration_0015_problematic_apps_warning": "Ten en conta que se detectaron as seguintes apps que poderían ser problemáticas. Semella que non foron instaladas usando o catálogo de YunoHost, ou non están marcadas como 'funcionais'. En consecuencia, non se pode garantir que seguirán funcionando após a actualización: {problematic_apps}", "diagnosis_http_localdomain": "O dominio {domain}, cun TLD .local, non é de agardar que sexa accesible desde o exterior da rede local.", - "diagnosis_dns_specialusedomain": "O dominio {domain} baséase un dominio de nivel alto e uso especial (TLD) polo que non é de agardar que realmente teña rexistros DNS." + "diagnosis_dns_specialusedomain": "O dominio {domain} baséase un dominio de nivel alto e uso especial (TLD) polo que non é de agardar que realmente teña rexistros DNS.", + "upnp_enabled": "UPnP activado", + "upnp_disabled": "UPnP desactivado", + "permission_creation_failed": "Non se creou o permiso '{permission}': {error}", + "permission_created": "Creado o permiso '{permission}'", + "permission_cannot_remove_main": "Non está permitido eliminar un permiso principal", + "permission_already_up_to_date": "Non se actualizou o permiso porque as solicitudes de adición/retirada xa coinciden co estado actual.", + "permission_already_exist": "Xa existe o permiso '{permission}'", + "permission_already_disallowed": "O grupo '{group}' xa ten o permiso '{permission}' desactivado", + "permission_already_allowed": "O grupo '{group}' xa ten o permiso '{permission}' activado", + "pattern_password_app": "Lamentámolo, os contrasinais non poden conter os seguintes caracteres: {forbidden_chars}", + "pattern_username": "Só admite caracteres alfanuméricos en minúscula e trazo baixo", + "pattern_positive_number": "Ten que ser un número positivo", + "pattern_port_or_range": "Debe ser un número válido de porto (entre 0-65535) ou rango de portos (ex. 100:200)", + "pattern_password": "Ten que ter polo menos 3 caracteres", + "pattern_mailbox_quota": "Ten que ser un tamaño co sufixo b/k/M/G/T ou 0 para non ter unha cota", + "pattern_lastname": "Ten que ser un apelido válido", + "pattern_firstname": "Ten que ser un nome válido", + "pattern_email": "Ten que ser un enderezo de email válido, sen o símbolo '+' (ex. persoa@exemplo.com)", + "pattern_email_forward": "Ten que ser un enderezo de email válido, está aceptado o símbolo '+' (ex. persoa+etiqueta@exemplo.com)", + "pattern_domain": "Ten que ser un nome de dominio válido (ex. dominiopropio.org)", + "pattern_backup_archive_name": "Ten que ser un nome de ficheiro válido con 30 caracteres como máximo, alfanuméricos ou só caracteres -_.", + "password_too_simple_4": "O contrasinal debe ter 12 caracteres como mínimo e conter un díxito, maiúsculas, minúsculas e caracteres especiais", + "password_too_simple_3": "O contrasinal debe ter 8 caracteres como mínimo e conter un díxito, maiúsculas, minúsculas e caracteres especiais", + "password_too_simple_2": "O contrasinal debe ter 8 caracteres como mínimo e conter un díxito, maiúsculas e minúsculas", + "password_listed": "Este contrasinal está entre os máis utilizados no mundo. Por favor elixe outro que sexa máis orixinal.", + "packages_upgrade_failed": "Non se puideron actualizar tódolos paquetes", + "operation_interrupted": "Foi interrumpida manualmente a operación?", + "invalid_number": "Ten que ser un número", + "not_enough_disk_space": "Non hai espazo libre abondo en '{path}'", + "migrations_to_be_ran_manually": "A migración {id} ten que ser executada manualmente. Vaite a Ferramentas → Migracións na páxina webadmin, ou executa `yunohost tools migrations run`.", + "migrations_success_forward": "Migración {id} completada", + "migrations_skip_migration": "Omitindo migración {id}...", + "migrations_running_forward": "Realizando migración {id}...", + "migrations_pending_cant_rerun": "Esas migracións están pendentes, polo que non ser executadas outra vez: {ids}", + "migrations_not_pending_cant_skip": "Esas migracións non están pendentes, polo que non poden ser omitidas: {ids}", + "migrations_no_such_migration": "Non hai migración co nome '{id}'", + "migrations_no_migrations_to_run": "Sen migracións a executar", + "migrations_need_to_accept_disclaimer": "Para executar a migración {id}, tes que aceptar o seguinte aviso:\n---\n{disclaimer}\n---\nSe aceptas executar a migración, por favor volve a executar o comando coa opción '--accept-disclaimer'.", + "migrations_must_provide_explicit_targets": "Debes proporcionar obxectivos explícitos ao utilizar '--skip' ou '--force-rerun'", + "migrations_migration_has_failed": "A migración {id} non se completou, abortando. Erro: {exception}", + "migrations_loading_migration": "Cargando a migración {id}...", + "migrations_list_conflict_pending_done": "Non podes usar ao mesmo tempo '--previous' e '--done'.", + "migrations_exclusive_options": "'--auto', '--skip', e '--force-rerun' son opcións que se exclúen unhas a outras.", + "migrations_failed_to_load_migration": "Non se cargou a migración {id}: {error}", + "migrations_dependencies_not_satisfied": "Executar estas migracións: '{dependencies_id}', antes da migración {id}.", + "migrations_cant_reach_migration_file": "Non se pode acceder aos ficheiros de migración na ruta '%s'" } From 5f8a97dd719b72e203be3d382455eccd28fef3e9 Mon Sep 17 00:00:00 2001 From: ppr Date: Wed, 25 Aug 2021 16:07:57 +0000 Subject: [PATCH 079/114] Translated using Weblate (French) Currently translated at 99.6% (637 of 639 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 4ac519c11..e78ea4e1f 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -156,7 +156,7 @@ "certmanager_attempt_to_renew_nonLE_cert": "Le certificat pour le domaine {domain} n’est pas émis par Let’s Encrypt. Impossible de le renouveler automatiquement !", "certmanager_attempt_to_renew_valid_cert": "Le certificat pour le domaine {domain} n’est pas sur le point d’expirer ! (Vous pouvez utiliser --force si vous savez ce que vous faites)", "certmanager_domain_http_not_working": "Le domaine {domain} 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_dns_ip_differs_from_public_ip": "L'enregistrement DNS du domaine '{domain}' est différent de l’adresse 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_domain_dns_ip_differs_from_public_ip": "Les enregistrements DNS du domaine '{domain}' est différent de l’adresse 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 s’est mal passé lors de la tentative d’ouverture du certificat actuel pour le domaine {domain} (fichier : {file}), la cause est : {reason}", "certmanager_cert_install_success_selfsigned": "Le certificat auto-signé est maintenant installé pour le domaine « {domain} »", "certmanager_cert_install_success": "Le certificat Let’s Encrypt est maintenant installé pour le domaine « {domain} »", @@ -217,10 +217,10 @@ "backup_couldnt_bind": "Impossible de lier {src} avec {dest}.", "domain_dns_conf_is_just_a_recommendation": "Cette commande vous montre la configuration *recommandée*. Elle ne configure pas le DNS pour vous. Il est de votre ressort de configurer votre zone DNS chez votre registrar/fournisseur conformément à cette recommandation.", "migrations_cant_reach_migration_file": "Impossible d’accéder aux fichiers de migration via le chemin '%s'", - "migrations_loading_migration": "Chargement de la migration {id}...", + "migrations_loading_migration": "Chargement de la migration {id} ...", "migrations_migration_has_failed": "La migration {id} a échoué avec l’exception {exception} : annulation", "migrations_no_migrations_to_run": "Aucune migration à lancer", - "migrations_skip_migration": "Ignorer et passer la migration {id}...", + "migrations_skip_migration": "Ignorer et passer la migration {id} ...", "server_shutdown": "Le serveur va s’éteindre", "server_shutdown_confirm": "Le serveur va être éteint immédiatement, le voulez-vous vraiment ? [{answers}]", "server_reboot": "Le serveur va redémarrer", @@ -342,7 +342,7 @@ "regenconf_would_be_updated": "La configuration aurait dû être mise à jour pour la catégorie '{category}'", "regenconf_dry_pending_applying": "Vérification de la configuration en attente qui aurait été appliquée pour la catégorie '{category}'…", "regenconf_failed": "Impossible de régénérer la configuration pour la ou les catégorie(s) : '{categories}'", - "regenconf_pending_applying": "Applique la configuration en attente pour la catégorie '{category}'...", + "regenconf_pending_applying": "Applique la configuration en attente pour la catégorie '{category}' ...", "service_regen_conf_is_deprecated": "'yunohost service regen-conf' est obsolète ! Veuillez plutôt utiliser 'yunohost tools regen-conf' à la place.", "tools_upgrade_at_least_one": "Veuillez spécifier '--apps' ou '--system'", "tools_upgrade_cant_both": "Impossible de mettre à niveau le système et les applications en même temps", @@ -381,7 +381,7 @@ "migrations_already_ran": "Ces migrations sont déjà effectuées : {ids}", "migrations_dependencies_not_satisfied": "Exécutez ces migrations : '{dependencies_id}', avant migration {id}.", "migrations_failed_to_load_migration": "Impossible de charger la migration {id} : {error}", - "migrations_running_forward": "Exécution de la migration {id}...", + "migrations_running_forward": "Exécution de la migration {id} ...", "migrations_success_forward": "Migration {id} terminée", "operation_interrupted": "L'opération a-t-elle été interrompue manuellement ?", "permission_already_exist": "L’autorisation '{permission}' existe déjà", @@ -554,24 +554,24 @@ "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 l’espé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 nécessaires...", - "migration_0015_specific_upgrade": "Démarrage de la mise à jour des paquets du système qui doivent être mis à jour séparément...", + "migration_0015_cleaning_up": "Nettoyage du cache et des paquets qui ne sont plus nécessaires ...", + "migration_0015_specific_upgrade": "Démarrage 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_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_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 yunohost dyndns update --force.", "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}", - "global_settings_setting_backup_compress_tar_archives": "Compresser les archives (.tar.gz) au lieu de créer des archives non-compressées lors de la création des backups. N.B. Activer cette option implique de créer des archives plus légères, mais aussi d'avoir des procédures de backup significativement plus longues et plus gourmandes en CPU.", + "global_settings_setting_backup_compress_tar_archives": "Compresser les archives (.tar.gz) au lieu de créer des archives non-compressées lors de la création des sauvegardes. N.B. Activer cette option implique de créer des archives plus légères, mais aussi d'avoir des procédures de sauvegarde significativement plus longues et plus gourmandes en CPU.", "migration_description_0018_xtable_to_nftable": "Migrer les anciennes règles de trafic réseau vers le nouveau système basé sur nftables", "service_description_php7.3-fpm": "Exécute les applications écrites en PHP avec NGINX", "migration_0018_failed_to_reset_legacy_rules": "La réinitialisation des règles iptable par défaut a échoué : {error}", @@ -612,7 +612,7 @@ "additional_urls_already_removed": "URL supplémentaire '{url}' déjà supprimées pour la permission '{permission}'", "invalid_number": "Doit être un nombre", "migration_description_0019_extend_permissions_features": "Étendre et retravailler le système de gestion des permissions applicatives", - "diagnosis_basesystem_hardware_model": "Le modèle du serveur est {model}", + "diagnosis_basesystem_hardware_model": "Le modèle/architecture du serveur est {model}", "diagnosis_backports_in_sources_list": "Il semble qu'apt (le gestionnaire de paquets) soit configuré pour utiliser le dépôt des rétroportages (backports). A moins que vous ne sachiez vraiment ce que vous faites, nous vous déconseillons fortement d'installer des paquets provenant des rétroportages, car cela risque de créer des instabilités ou des conflits sur votre système.", "postinstall_low_rootfsspace": "Le système de fichiers a une taille totale inférieure à 10 Go, ce qui est préoccupant et devrait attirer votre attention ! Vous allez certainement arriver à court d'espace disque (très) rapidement ! Il est recommandé d'avoir au moins 16 Go à la racine pour ce système de fichiers. Si vous voulez installer YunoHost malgré cet avertissement, relancez la post-installation avec --force-diskspace", "domain_remove_confirm_apps_removal": "Le retrait de ce domaine retirera aussi ces applications :\n{apps}\n\nÊtes vous sûr de vouloir cela ? [{answers}]", From 46916b57d4500d35eb12eb03a3d48d5a73929ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M?= Date: Thu, 26 Aug 2021 04:32:57 +0000 Subject: [PATCH 080/114] Translated using Weblate (Galician) Currently translated at 81.8% (523 of 639 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/gl/ --- locales/gl.json | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/locales/gl.json b/locales/gl.json index 46665b870..5c0bec604 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -505,5 +505,22 @@ "migrations_exclusive_options": "'--auto', '--skip', e '--force-rerun' son opcións que se exclúen unhas a outras.", "migrations_failed_to_load_migration": "Non se cargou a migración {id}: {error}", "migrations_dependencies_not_satisfied": "Executar estas migracións: '{dependencies_id}', antes da migración {id}.", - "migrations_cant_reach_migration_file": "Non se pode acceder aos ficheiros de migración na ruta '%s'" + "migrations_cant_reach_migration_file": "Non se pode acceder aos ficheiros de migración na ruta '%s'", + "regenconf_file_manually_removed": "O ficheiro de configuración '{conf}' foi eliminado manualmente e non será creado", + "regenconf_file_manually_modified": "O ficheiro de configuración '{conf}' foi modificado manualmente e non vai ser actualizado", + "regenconf_file_kept_back": "Era de agardar que o ficheiro de configuración '{conf}' fose eliminado por regen-conf (categoría {category}) mais foi mantido.", + "regenconf_file_copy_failed": "Non se puido copiar o novo ficheiro de configuración '{new}' a '{conf}'", + "regenconf_file_backed_up": "Ficheiro de configuración '{conf}' copiado a '{backup}'", + "postinstall_low_rootfsspace": "O sistema de ficheiros raiz ten un espazo total menor de 10GB, que é pouco! Probablemente vas quedar sen espazo moi pronto! É recomendable ter polo menos 16GB para o sistema raíz. Se queres instalar YunoHost obviando este aviso, volve a executar a postinstalación con --force-diskspace", + "port_already_opened": "O porto {port:d} xa está aberto para conexións {ip_version}", + "port_already_closed": "O porto {port:d} xa está pechado para conexións {ip_version}", + "permission_require_account": "O permiso {permission} só ten sentido para usuarias cunha conta, e por tanto non pode concederse a visitantes.", + "permission_protected": "O permiso {permission} está protexido. Non podes engadir ou eliminar o grupo visitantes a/de este permiso.", + "permission_updated": "Permiso '{permission}' actualizado", + "permission_update_failed": "Non se actualizou o permiso '{permission}': {error}", + "permission_not_found": "Non se atopa o permiso '{permission}'", + "permission_deletion_failed": "Non se puido eliminar o permiso '{permission}': {error}", + "permission_deleted": "O permiso '{permission}' foi eliminado", + "permission_cant_add_to_all_users": "O permiso {permission} non pode ser concecido a tódalas usuarias.", + "permission_currently_allowed_for_all_users": "Este permiso está concedido actualmente a tódalas usuarias ademáis de a outros grupos. Probablemente queiras ben eliminar o permiso 'all_users' ou ben eliminar os outros grupos que teñen permiso." } From 569dc24267a499d9c231154ed64feab3a3c38fa9 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Thu, 26 Aug 2021 23:01:14 +0200 Subject: [PATCH 081/114] remove unused imports --- data/hooks/diagnosis/80-apps.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/data/hooks/diagnosis/80-apps.py b/data/hooks/diagnosis/80-apps.py index ce54faef1..4ab5a6c0d 100644 --- a/data/hooks/diagnosis/80-apps.py +++ b/data/hooks/diagnosis/80-apps.py @@ -1,16 +1,10 @@ #!/usr/bin/env python import os -import re -from yunohost.app import app_info, app_list -from moulinette.utils.filesystem import read_file +from yunohost.app import app_list -from yunohost.settings import settings_get from yunohost.diagnosis import Diagnoser -from yunohost.regenconf import _get_regenconf_infos, _calculate_hash -from moulinette.utils.filesystem import read_file - class AppDiagnoser(Diagnoser): From f7258dd3a6cf91cc07da2d06a960ae2af1fc9ee8 Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Fri, 27 Aug 2021 01:17:11 +0200 Subject: [PATCH 082/114] [fix] Another tmp nightmare original idea from Aleks --- data/hooks/conf_regen/06-slapd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 3fa3a0fd2..b2439dcf9 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -2,7 +2,7 @@ set -e -tmp_backup_dir_file="/tmp/slapd-backup-dir.txt" +tmp_backup_dir_file="/root/slapd-backup-dir.txt" config="/usr/share/yunohost/templates/slapd/config.ldif" db_init="/usr/share/yunohost/templates/slapd/db_init.ldif" From 3c646b3d6a647bcd33cad8d929eda9300124f97b Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Fri, 27 Aug 2021 01:17:11 +0200 Subject: [PATCH 083/114] [fix] Another tmp nightmare original idea from Aleks --- data/hooks/conf_regen/06-slapd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 3fa3a0fd2..b2439dcf9 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -2,7 +2,7 @@ set -e -tmp_backup_dir_file="/tmp/slapd-backup-dir.txt" +tmp_backup_dir_file="/root/slapd-backup-dir.txt" config="/usr/share/yunohost/templates/slapd/config.ldif" db_init="/usr/share/yunohost/templates/slapd/db_init.ldif" From eef5cd4da913705035767fa62d497096890f6c74 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 27 Aug 2021 01:34:10 +0200 Subject: [PATCH 084/114] Update changelog for 4.2.8.1 --- debian/changelog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/debian/changelog b/debian/changelog index b1894758a..48f3bbdca 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +yunohost (4.2.8.1) stable; urgency=low + + - [fix] Safer location for slapd backup during hdb/mdb migration (3c646b3d) + + Thanks to all contributors <3 ! (ljf) + + -- Alexandre Aubin Fri, 27 Aug 2021 01:32:16 +0200 + yunohost (4.2.8) stable; urgency=low - [fix] ynh_permission_has_user not behaving properly when checking if a group is allowed (f0590907) From 5a22aa883f597f84d5a770fbd0d0705eb31e99b2 Mon Sep 17 00:00:00 2001 From: Tymofii-Lytvynenko Date: Fri, 27 Aug 2021 13:25:33 +0000 Subject: [PATCH 085/114] Translated using Weblate (Ukrainian) Currently translated at 5.3% (34 of 639 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/uk/ --- locales/uk.json | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/locales/uk.json b/locales/uk.json index 9e26dfeeb..c85b4fd0a 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -1 +1,36 @@ -{} \ No newline at end of file +{ + "app_manifest_install_ask_domain": "Оберіть домен, в якому треба встановити цей застосунок", + "app_manifest_invalid": "Щось не так з маніфестом застосунку: {error}", + "app_location_unavailable": "Ця URL-адреса або недоступна, або конфліктує з уже встановленим застосунком (застосунками):\n{apps}", + "app_label_deprecated": "Ця команда застаріла! Будь ласка, використовуйте нову команду 'yunohost user permission update' для управління заголовком застосунку.", + "app_make_default_location_already_used": "Неможливо зробити '{app}' типовим застосунком на домені, '{domain}' вже використовується '{other_app}'", + "app_install_script_failed": "Сталася помилка в скрипті встановлення застосунку", + "app_install_failed": "Неможливо встановити {app}: {error}", + "app_install_files_invalid": "Ці файли не можуть бути встановлені", + "app_id_invalid": "Неприпустимий ID застосунку", + "app_full_domain_unavailable": "Вибачте, цей застосунок повинен бути встановлений на власному домені, але інші застосунки вже встановлені на домені '{domain}'. Замість цього ви можете використовувати піддомен, призначений для цього застосунку.", + "app_extraction_failed": "Не вдалося витягти файли встановлення", + "app_change_url_success": "URL-адреса {app} тепер {domain}{path}", + "app_change_url_no_script": "Застосунок '{app_name}' поки не підтримує зміну URL-адрес. Можливо, вам слід оновити його.", + "app_change_url_identical_domains": "Старий і новий domain/url_path збігаються ('{domain}{path}'), нічого робити не треба.", + "app_change_url_failed_nginx_reload": "Не вдалося перезавантажити NGINX. Ось результат 'nginx -t':\n{nginx_errors}", + "app_argument_required": "Аргумент '{name}' необхідний", + "app_argument_password_no_default": "Помилка під час розбору аргументу пароля '{name}': аргумент пароля не може мати типове значення з причин безпеки", + "app_argument_invalid": "Виберіть правильне значення для аргументу '{name}': {error}", + "app_argument_choice_invalid": "Використовуйте один з цих варіантів '{choices}' для аргументу '{name}'", + "app_already_up_to_date": "{app} має найостаннішу версію", + "app_already_installed_cant_change_url": "Цей застосунок уже встановлено. URL-адреса не може бути змінена тільки цією функцією. Перевірте в `app changeurl`, якщо вона доступна.", + "app_already_installed": "{app} уже встановлено", + "app_action_broke_system": "Ця дія, схоже, порушила роботу наступних важливих служб: {services}", + "app_action_cannot_be_ran_because_required_services_down": "Для виконання цієї дії повинні бути запущені наступні необхідні служби: {services}. Спробуйте перезапустити їх, щоб продовжити (і, можливо, з'ясувати, чому вони не працюють).", + "already_up_to_date": "Нічого не потрібно робити. Все вже актуально.", + "admin_password_too_long": "Будь ласка, виберіть пароль коротше 127 символів", + "admin_password_changed": "Пароль адміністратора було змінено", + "admin_password_change_failed": "Неможливо змінити пароль", + "admin_password": "Пароль адміністратора", + "additional_urls_already_removed": "Додаткова URL-адреса '{url}' вже видалена в додатковій URL-адресі для дозволу '{permission}'", + "additional_urls_already_added": "Додаткова URL-адреса '{url}' вже додана в додаткову URL-адресу для дозволу '{permission}'", + "action_invalid": "Неприпустима дія '{action}'", + "aborting": "Переривання.", + "diagnosis_description_web": "Мережа" +} From a2d28b21de49c49bb318be07aa89532163e51571 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 27 Aug 2021 16:34:07 +0200 Subject: [PATCH 086/114] ynh_multimedia: local can only be used in a function --- data/hooks/post_user_create/ynh_multimedia | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/post_user_create/ynh_multimedia b/data/hooks/post_user_create/ynh_multimedia index 2be3f42d4..26282cdc9 100644 --- a/data/hooks/post_user_create/ynh_multimedia +++ b/data/hooks/post_user_create/ynh_multimedia @@ -16,7 +16,7 @@ mkdir -p "$MEDIA_DIRECTORY/$user/eBook" ln -sfn "$MEDIA_DIRECTORY/share" "$MEDIA_DIRECTORY/$user/Share" # Création du lien symbolique dans le home de l'utilisateur. #link will only be created if the home directory of the user exists and if it's located in '/home' folder -local user_home="$(getent passwd $user | cut -d: -f6 | grep '^/home/')" +user_home="$(getent passwd $user | cut -d: -f6 | grep '^/home/')" if [[ -d "$user_home" ]]; then ln -sfn "$MEDIA_DIRECTORY/$user" "$user_home/Multimedia" fi From 74e3afd45e5c575984f59cade5d25f2a332678d3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 27 Aug 2021 17:31:57 +0200 Subject: [PATCH 087/114] French wording/typos --- locales/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index e78ea4e1f..7719e1390 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -156,7 +156,7 @@ "certmanager_attempt_to_renew_nonLE_cert": "Le certificat pour le domaine {domain} n’est pas émis par Let’s Encrypt. Impossible de le renouveler automatiquement !", "certmanager_attempt_to_renew_valid_cert": "Le certificat pour le domaine {domain} n’est pas sur le point d’expirer ! (Vous pouvez utiliser --force si vous savez ce que vous faites)", "certmanager_domain_http_not_working": "Le domaine {domain} 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_dns_ip_differs_from_public_ip": "Les enregistrements DNS du domaine '{domain}' est différent de l’adresse 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_domain_dns_ip_differs_from_public_ip": "Les enregistrements DNS du domaine '{domain}' sont différents de l’adresse 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 s’est mal passé lors de la tentative d’ouverture du certificat actuel pour le domaine {domain} (fichier : {file}), la cause est : {reason}", "certmanager_cert_install_success_selfsigned": "Le certificat auto-signé est maintenant installé pour le domaine « {domain} »", "certmanager_cert_install_success": "Le certificat Let’s Encrypt est maintenant installé pour le domaine « {domain} »", @@ -571,7 +571,7 @@ "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 yunohost dyndns update --force.", "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}", - "global_settings_setting_backup_compress_tar_archives": "Compresser les archives (.tar.gz) au lieu de créer des archives non-compressées lors de la création des sauvegardes. N.B. Activer cette option implique de créer des archives plus légères, mais aussi d'avoir des procédures de sauvegarde significativement plus longues et plus gourmandes en CPU.", + "global_settings_setting_backup_compress_tar_archives": "Compresser les archives (.tar.gz) au lieu de créer des archives non-compressées lors de la création des sauvegardes. N.B. Activer cette option permet de créer des archives plus légères, mais aussi d'avoir des procédures de sauvegarde significativement plus longues et plus gourmandes en CPU.", "migration_description_0018_xtable_to_nftable": "Migrer les anciennes règles de trafic réseau vers le nouveau système basé sur nftables", "service_description_php7.3-fpm": "Exécute les applications écrites en PHP avec NGINX", "migration_0018_failed_to_reset_legacy_rules": "La réinitialisation des règles iptable par défaut a échoué : {error}", From c62e168689c449483d4cd865249d82cd6a3cde3a Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Fri, 27 Aug 2021 15:53:09 +0000 Subject: [PATCH 088/114] [CI] Format code --- src/yunohost/service.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 671447067..fb12e9053 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -37,7 +37,13 @@ from moulinette import m18n from yunohost.utils.error import YunohostError, YunohostValidationError from moulinette.utils.process import check_output from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import read_file, append_to_file, write_to_file, read_yaml, write_to_yaml +from moulinette.utils.filesystem import ( + read_file, + append_to_file, + write_to_file, + read_yaml, + write_to_yaml, +) MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock" @@ -680,9 +686,11 @@ def _get_services(): services.update(read_yaml(SERVICES_CONF) or {}) - services = {name: infos - for name, infos in services.items() - if name not in legacy_keys_to_delete} + services = { + name: infos + for name, infos in services.items() + if name not in legacy_keys_to_delete + } except Exception: return {} From 2885fef1567bbd6db16af53f97b08b455f574761 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 27 Aug 2021 11:26:45 +0000 Subject: [PATCH 089/114] Translated using Weblate (German) Currently translated at 99.0% (633 of 639 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/de.json b/locales/de.json index ea20c7d7f..0580eac1d 100644 --- a/locales/de.json +++ b/locales/de.json @@ -378,7 +378,7 @@ "diagnosis_mail_ehlo_wrong_details": "Die vom Remote-Diagnose-Server per IPv{ipversion} empfangene EHLO weicht von der Domäne Ihres Servers ab.
Empfangene EHLO: {wrong_ehlo}
Erwartet: {right_ehlo}
Die geläufigste Ursache für dieses Problem ist, dass der Port 25 nicht korrekt auf Ihren Server weitergeleitet wird. Sie können sich zusätzlich auch versichern, dass keine Firewall oder Reverse-Proxy interferiert.", "diagnosis_mail_ehlo_bad_answer_details": "Das könnte daran liegen, dass anstelle Ihres Servers eine andere Maschine antwortet.", "ask_user_domain": "Domäne, welche für die E-Mail-Adresse und den XMPP-Account des Benutzers verwendet werden soll", - "app_manifest_install_ask_is_public": "Soll diese Applikation für anonyme Benutzer sichtbar sein?", + "app_manifest_install_ask_is_public": "Soll diese Applikation für anonyme Benutzer:innen sichtbar sein?", "app_manifest_install_ask_admin": "Wählen Sie einen Administrator für diese Applikation", "app_manifest_install_ask_path": "Wählen Sie den Pfad, in welchem die Applikation installiert werden soll", "diagnosis_mail_blacklist_listed_by": "Ihre IP-Adresse oder Domäne {item} ist auf der Blacklist auf {blacklist_name}", @@ -635,4 +635,4 @@ "global_settings_setting_security_webadmin_allowlist_enabled": "Erlaube nur bestimmten IP-Adressen den Zugriff auf die Verwaltungsseite.", "disk_space_not_sufficient_update": "Es ist nicht genügend Speicherplatz frei, um diese Applikation zu aktuallisieren", "disk_space_not_sufficient_install": "Es ist nicht genügend Speicherplatz frei, um diese Applikation zu installieren" -} \ No newline at end of file +} From 23b06252e3518eacd061e26fd67f4d7c444f2b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M?= Date: Fri, 27 Aug 2021 13:38:21 +0000 Subject: [PATCH 090/114] Translated using Weblate (Galician) Currently translated at 85.1% (544 of 639 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/gl/ --- locales/gl.json | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/locales/gl.json b/locales/gl.json index 5c0bec604..10322d2e8 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -75,7 +75,7 @@ "app_manifest_install_ask_is_public": "Debería esta app estar exposta ante visitantes anónimas?", "app_manifest_install_ask_admin": "Elixe unha usuaria administradora para esta app", "app_manifest_install_ask_password": "Elixe un contrasinal de administración para esta app", - "app_manifest_install_ask_path": "Elixe a ruta onde queres instalar esta app", + "app_manifest_install_ask_path": "Elixe a ruta URL (após o dominio) onde será instalada esta app", "app_manifest_install_ask_domain": "Elixe o dominio onde queres instalar esta app", "app_manifest_invalid": "Hai algún erro no manifesto da app: {error}", "app_location_unavailable": "Este URL ou ben non está dispoñible ou entra en conflito cunha app(s) xa instalada:\n{apps}", @@ -436,7 +436,7 @@ "log_regen_conf": "Rexerar configuración do sistema '{}'", "log_letsencrypt_cert_renew": "Anovar certificado Let's Encrypt para '{}'", "log_selfsigned_cert_install": "Instalar certificado auto-asinado para o dominio '{}'", - "log_permission_url": "Actualizar url relativo ao permiso '{}'", + "log_permission_url": "Actualizar URL relativo ao permiso '{}'", "migration_0015_general_warning": "Ten en conta que a migración é unha operación delicada. O equipo YunoHost esforzouse revisando e comprobandoa, aínda así algo podería fallar en partes do teu sistema ou as súas apps.\n\nPor tanto, é recomendable:\n- realiza unha copia de apoio de tódolos datos ou apps importantes. Máis info en https://yunohost.org/backup;\n - ten paciencia tras iniciar o proceso: dependendo da túa conexión de internet e hardware podería demorar varias horas a actualización de tódolos compoñentes.", "migration_0015_system_not_fully_up_to_date": "O teu sistema non está ao día. Realiza unha actualización común antes de realizar a migración a Buster.", "migration_0015_not_enough_free_space": "Queda moi pouco espazo en /var/! Deberías ter polo menos 1GB libre para realizar a migración.", @@ -522,5 +522,26 @@ "permission_deletion_failed": "Non se puido eliminar o permiso '{permission}': {error}", "permission_deleted": "O permiso '{permission}' foi eliminado", "permission_cant_add_to_all_users": "O permiso {permission} non pode ser concecido a tódalas usuarias.", - "permission_currently_allowed_for_all_users": "Este permiso está concedido actualmente a tódalas usuarias ademáis de a outros grupos. Probablemente queiras ben eliminar o permiso 'all_users' ou ben eliminar os outros grupos que teñen permiso." + "permission_currently_allowed_for_all_users": "Este permiso está concedido actualmente a tódalas usuarias ademáis de a outros grupos. Probablemente queiras ben eliminar o permiso 'all_users' ou ben eliminar os outros grupos que teñen permiso.", + "restore_failed": "Non se puido restablecer o sistema", + "restore_extracting": "Extraendo os ficheiros necesarios desde o arquivo…", + "restore_confirm_yunohost_installed": "Tes a certeza de querer restablecer un sistema xa instalado? [{answers}]", + "restore_complete": "Restablecemento completado", + "restore_cleaning_failed": "Non se puido despexar o directorio temporal de restablecemento", + "restore_backup_too_old": "Este arquivo de apoio non pode ser restaurado porque procede dunha versión YunoHost demasiado antiga.", + "restore_already_installed_apps": "As seguintes apps non se poden restablecer porque xa están instaladas: {apps}", + "restore_already_installed_app": "Unha app con ID '{app}' xa está instalada", + "regex_with_only_domain": "Agora xa non podes usar un regex para o dominio, só para ruta", + "regex_incompatible_with_tile": "/!\\ Empacadoras! O permiso '{permission}' agora ten show_tile establecido como 'true' polo que non podes definir o regex URL como URL principal", + "regenconf_need_to_explicitly_specify_ssh": "A configuración ssh foi modificada manualmente, pero tes que indicar explícitamente a categoría 'ssh' con --force para realmente aplicar os cambios.", + "regenconf_pending_applying": "Aplicando a configuración pendente para categoría '{category}'...", + "regenconf_failed": "Non se rexenerou a configuración para a categoría(s): {categories}", + "regenconf_dry_pending_applying": "Comprobando as configuracións pendentes que deberían aplicarse á categoría '{category}'…", + "regenconf_would_be_updated": "A configuración debería ser actualizada para a categoría '{category}'", + "regenconf_updated": "Configuración actualizada para '{category}'", + "regenconf_up_to_date": "A configuración xa está ao día para a categoría '{category}'", + "regenconf_now_managed_by_yunohost": "O ficheiro de configuración '{conf}' agora está xestionado por YunoHost (categoría {category}).", + "regenconf_file_updated": "Actualizado o ficheiro de configuración '{conf}'", + "regenconf_file_removed": "Eliminado o ficheiro de configuración '{conf}'", + "regenconf_file_remove_failed": "Non se puido eliminar o ficheiro de configuración '{conf}'" } From 75879d486df336cac2745b5bfbf8586a22d45997 Mon Sep 17 00:00:00 2001 From: Tymofii-Lytvynenko Date: Fri, 27 Aug 2021 14:18:43 +0000 Subject: [PATCH 091/114] Translated using Weblate (Ukrainian) Currently translated at 5.4% (35 of 639 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/uk/ --- locales/uk.json | 607 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 606 insertions(+), 1 deletion(-) diff --git a/locales/uk.json b/locales/uk.json index c85b4fd0a..71d7ee897 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -32,5 +32,610 @@ "additional_urls_already_added": "Додаткова URL-адреса '{url}' вже додана в додаткову URL-адресу для дозволу '{permission}'", "action_invalid": "Неприпустима дія '{action}'", "aborting": "Переривання.", - "diagnosis_description_web": "Мережа" + "diagnosis_description_web": "Мережа", + "service_reloaded_or_restarted": "Служба '{service}' була перезавантажена або перезапущено", + "service_reload_or_restart_failed": "Не вдалося перезавантажити або перезапустити службу '{service}' Recent service logs: {logs}", + "service_restarted": "Служба '{service}' перезапущено", + "service_restart_failed": "Не вдалося запустити службу '{service}' Недавні журнали служб: {logs}", + "service_reloaded": "Служба '{service}' перезавантажена", + "service_reload_failed": "Не вдалося перезавантажити службу '{service}' Останні журнали служби: {logs}", + "service_removed": "Служба '{service}' вилучена", + "service_remove_failed": "Не вдалося видалити службу '{service}'", + "service_regen_conf_is_deprecated": "'Yunohost service regen-conf' застарів! Будь ласка, використовуйте 'yunohost tools regen-conf' замість цього.", + "service_enabled": "Служба '{service}' тепер буде автоматично запускатися при завантаженні системи.", + "service_enable_failed": "Неможливо змусити службу '{service}' автоматично запускатися при завантаженні. Недавні журнали служб: {logs}", + "service_disabled": "Служба '{service}' більше не буде запускатися при завантаженні системи.", + "service_disable_failed": "Неможливо змусити службу '{service} \"не запускатися при завантаженні. Останні журнали служби: {logs}", + "service_description_yunohost-firewall": "Управляє відкритими і закритими портами підключення до сервісів", + "service_description_yunohost-api": "Управляє взаємодією між веб-інтерфейсом YunoHost і системою", + "service_description_ssh": "Дозволяє віддалено підключатися до сервера через термінал (протокол SSH)", + "service_description_slapd": "Зберігає користувачів, домени і пов'язану з ними інформацію", + "service_description_rspamd": "Фільтрує спам і інші функції, пов'язані з електронною поштою", + "service_description_redis-server": "Спеціалізована база даних, яка використовується для швидкого доступу до даних, черги завдань і зв'язку між програмами", + "service_description_postfix": "Використовується для відправки та отримання електронної пошти", + "service_description_php7.3-fpm": "Запускає програми, написані на мові програмування PHP, за допомогою NGINX", + "service_description_nginx": "Обслуговує або надає доступ до всіх веб-сайтів, розміщених на вашому сервері", + "service_description_mysql": "Зберігає дані додатків (база даних SQL)", + "service_description_metronome": "Служба захисту миттєвого обміну повідомленнями XMPP", + "service_description_fail2ban": "Захист від перебору та інших видів атак з Інтернету", + "service_description_dovecot": "Дозволяє поштовим клієнтам отримувати доступ до електронної пошти (через IMAP і POP3)", + "service_description_dnsmasq": "Обробляє дозвіл доменних імен (DNS)", + "service_description_yunomdns": "Дозволяє вам отримати доступ до вашого сервера, використовуючи 'yunohost.local' у вашій локальній мережі", + "service_cmd_exec_failed": "Не вдалося виконати команду '{команда}'", + "service_already_stopped": "Служба '{service}' вже зупинена", + "service_already_started": "Служба '{service}' вже запущена", + "service_added": "Служба '{service}' була додана", + "service_add_failed": "Не вдалося додати службу '{service}'", + "server_reboot_confirm": "Сервер негайно перезавантажиться, ви впевнені? [{Відповіді}]", + "server_reboot": "сервер перезавантажиться", + "server_shutdown_confirm": "Сервер буде негайно виключений, ви впевнені? [{Відповіді}].", + "server_shutdown": "сервер вимкнеться", + "root_password_replaced_by_admin_password": "Ваш кореневої пароль був замінений на пароль адміністратора.", + "root_password_desynchronized": "Пароль адміністратора був змінений, але YunoHost не зміг поширити це на пароль root!", + "restore_system_part_failed": "Не вдалося відновити системну частину '{part}'.", + "restore_running_hooks": "Запуск хуков відновлення…", + "restore_running_app_script": "Відновлення програми \"{app} '…", + "restore_removing_tmp_dir_failed": "Неможливо видалити старий тимчасовий каталог", + "restore_nothings_done": "Нічого не було відновлено", + "restore_not_enough_disk_space": "Недостатньо місця (простір: {free_space: d} B, необхідний простір: {needed_space: d} B, маржа безпеки: {margin: d} B)", + "restore_may_be_not_enough_disk_space": "Схоже, у вашій системі недостатньо місця (вільного: {free_space: d} B, необхідний простір: {needed_space: d} B, запас міцності: {margin: d} B)", + "restore_hook_unavailable": "Сценарій відновлення для '{part}' недоступним у вашій системі і в архіві його теж немає", + "restore_failed": "Не вдалося відновити систему", + "restore_extracting": "Витяг необхідних файлів з архіву…", + "restore_confirm_yunohost_installed": "Ви дійсно хочете відновити вже встановлену систему? [{Відповіді}].", + "restore_complete": "відновлення завершено", + "restore_cleaning_failed": "Не вдалося очистити тимчасовий каталог відновлення", + "restore_backup_too_old": "Цей архів резервних копій не може бути відновлений, бо він отриманий з дуже старою версією YunoHost.", + "restore_already_installed_apps": "Наступні програми не можуть бути відновлені, тому що вони вже встановлені: {apps}", + "restore_already_installed_app": "Додаток з ідентифікатором \"{app} 'вже встановлено", + "regex_with_only_domain": "Ви не можете використовувати regex для домену, тільки для шляху.", + "regex_incompatible_with_tile": "/! \\ Packagers! Дозвіл '{permission}' має значення show_tile 'true', тому ви не можете визначити regex URL в якості основного URL.", + "regenconf_need_to_explicitly_specify_ssh": "Конфігурація ssh була змінена вручну, але вам потрібно явно вказати категорію 'ssh' з --force, щоб застосувати зміни.", + "regenconf_pending_applying": "Застосування очікує конфігурації для категорії '{категорія}'...", + "regenconf_failed": "Не вдалося відновити конфігурацію для категорії (категорій): {categories}", + "regenconf_dry_pending_applying": "Перевірка очікує конфігурації, яка була б застосована для категорії '{категорія}'…", + "regenconf_would_be_updated": "Конфігурація була б оновлена для категорії '{категорія}'", + "regenconf_updated": "Конфігурація оновлена для категорії '{category}'", + "regenconf_up_to_date": "Конфігурація вже оновлена для категорії '{категорія}'", + "regenconf_now_managed_by_yunohost": "Конфігураційний файл '{conf}' тепер управляється YunoHost (категорія {category}).", + "regenconf_file_updated": "Конфігураційний файл '{conf}' оновлений", + "regenconf_file_removed": "Конфігураційний файл '{conf}' видалений", + "regenconf_file_remove_failed": "Неможливо видалити файл конфігурації '{conf}'", + "regenconf_file_manually_removed": "Конфігураційний файл '{conf}' був видалений вручну і не буде створено", + "regenconf_file_manually_modified": "Конфігураційний файл '{conf}' був змінений вручну і не буде оновлено", + "regenconf_file_kept_back": "Конфігураційний файл '{conf}' повинен був бути вилучений regen-conf (категорія {category}), але був збережений.", + "regenconf_file_copy_failed": "Не вдалося скопіювати новий файл конфігурації '{new}' в '{conf}'", + "regenconf_file_backed_up": "Конфігураційний файл '{conf}' збережений в '{backup}'", + "postinstall_low_rootfsspace": "Загальна площа кореневої файлової системи становить менше 10 ГБ, що викликає занепокоєння! Швидше за все, дисковий простір закінчиться дуже швидко! Рекомендується мати не менше 16 ГБ для кореневої файлової системи. Якщо ви хочете встановити YunoHost, незважаючи на це попередження, повторно запустіть постінсталляцію з параметром --force-diskspace", + "port_already_opened": "Порт {port: d} вже відкритий для з'єднань {ip_version}.", + "port_already_closed": "Порт {port: d} вже закритий для з'єднань {ip_version}.", + "permission_require_account": "Дозвіл {permission} має сенс тільки для користувачів, що мають обліковий запис, і тому не може бути включено для відвідувачів.", + "permission_protected": "Дозвіл {permission} захищено. Ви не можете додавати або видаляти групу відвідувачів в/з цього дозволу.", + "permission_updated": "Дозвіл '{permission}' оновлено", + "permission_update_failed": "Не вдалося оновити дозвіл '{permission}': {error}", + "permission_not_found": "Дозвіл '{permission}', не знайдено", + "permission_deletion_failed": "Не вдалося видалити дозвіл '{permission}': {помилка}", + "permission_deleted": "Дозвіл '{permission}' видалено", + "permission_cant_add_to_all_users": "Дозвіл {permission} не може бути додано всім користувачам.", + "permission_currently_allowed_for_all_users": "В даний час цей дозвіл надається всім користувачам на додаток до інших груп. Ймовірно, вам потрібно або видалити дозвіл 'all_users', або видалити інші групи, яким воно зараз надано.", + "permission_creation_failed": "Не вдалося створити дозвіл '{permission}': {error}", + "permission_created": "Дозвіл '{permission}' створено", + "permission_cannot_remove_main": "Видалення основного дозволу заборонено", + "permission_already_up_to_date": "Дозвіл не було оновлено, тому що запити на додавання/видалення вже відповідають поточному стану.", + "permission_already_exist": "Дозвіл '{permission}' вже існує", + "permission_already_disallowed": "Група '{group}' вже має дозвіл \"{permission} 'відключено", + "permission_already_allowed": "Для гурту \"{group} 'вже включено дозвіл' {permission} '", + "pattern_password_app": "На жаль, паролі не можуть містити такі символи: {forbidden_chars}", + "pattern_username": "Повинен складатися тільки з букв і цифр в нижньому регістрі і символів підкреслення.", + "pattern_positive_number": "Повинно бути позитивним числом", + "pattern_port_or_range": "Повинен бути дійсний номер порту (наприклад, 0-65535) або діапазон портів (наприклад, 100: 200).", + "pattern_password": "Повинен бути довжиною не менше 3 символів", + "pattern_mailbox_quota": "Повинен бути розмір з суфіксом b/k/M/G/T або 0, щоб не мати квоти.", + "pattern_lastname": "Повинна бути дійсне прізвище", + "pattern_firstname": "Повинно бути дійсне ім'я", + "pattern_email": "Повинен бути дійсною адресою електронної пошти, без символу '+' (наприклад, someone@example.com ).", + "pattern_email_forward": "Повинен бути дійсну адресу електронної пошти, символ '+' приймається (наприклад, someone+tag@example.com )", + "pattern_domain": "Повинно бути дійсне доменне ім'я (наприклад, my-domain.org)", + "pattern_backup_archive_name": "Повинно бути правильне ім'я файлу, що містить не більше 30 символів, тільки букви і цифри символи і символ -_.", + "password_too_simple_4": "Пароль повинен бути довжиною не менше 12 символів і містити цифру, верхні, нижні і спеціальні символи.", + "password_too_simple_3": "Пароль повинен бути довжиною не менше 8 символів і містити цифру, верхні, нижні і спеціальні символи", + "password_too_simple_2": "Пароль повинен складатися не менше ніж з 8 символів і містити цифру, верхній і нижній символи", + "password_too_simple_1": "Пароль повинен складатися не менше ніж з 8 символів", + "password_listed": "Цей пароль входить в число найбільш часто використовуваних паролів в світі. Будь ласка, виберіть щось більш унікальне.", + "packages_upgrade_failed": "Не вдалося оновити всі пакети", + "operation_interrupted": "Операція була перервана вручну?", + "invalid_number": "Повинно бути число", + "not_enough_disk_space": "Недостатньо вільного місця на \"{шлях} '.", + "migrations_to_be_ran_manually": "Міграція {id} повинна бути запущена вручну. Будь ласка, перейдіть в розділ Інструменти → Міграції на сторінці веб-адміністратора або виконайте команду `yunohost tools migrations run`.", + "migrations_success_forward": "Міграція {id} завершена", + "migrations_skip_migration": "Пропуск міграції {id}...", + "migrations_running_forward": "Виконання міграції {id}...", + "migrations_pending_cant_rerun": "Ці міграції ще не завершені, тому не можуть бути запущені знову: {ids}", + "migrations_not_pending_cant_skip": "Ці міграції не очікують виконання, тому не можуть бути пропущені: {ids}", + "migrations_no_such_migration": "Не існує міграції під назвою '{id}'.", + "migrations_no_migrations_to_run": "Немає міграцій для запуску", + "migrations_need_to_accept_disclaimer": "Щоб запустити міграцію {id}, ви повинні прийняти наступний відмова від відповідальності: --- {disclaimer} --- Якщо ви згодні запустити міграцію, будь ласка, повторіть команду з опцією '--accept-disclaimer'.", + "migrations_must_provide_explicit_targets": "Ви повинні вказати явні цілі при використанні '--skip' або '--force-rerun'.", + "migrations_migration_has_failed": "Міграція {id} не завершена, переривається. Помилка: {exception}.", + "migrations_loading_migration": "Завантаження міграції {id}...", + "migrations_list_conflict_pending_done": "Ви не можете одночасно використовувати '--previous' і '--done'.", + "migrations_exclusive_options": "'--Auto', '--skip' і '--force-rerun' є взаємовиключними опціями.", + "migrations_failed_to_load_migration": "Не вдалося завантажити міграцію {id}: {error}", + "migrations_dependencies_not_satisfied": "Запустіть ці міграції: '{dependencies_id}', перед міграцією {id}.", + "migrations_cant_reach_migration_file": "Не вдалося отримати доступ до файлів міграцій по шляху '% s'.", + "migrations_already_ran": "Ці міграції вже виконані: {ids}", + "migration_0019_slapd_config_will_be_overwritten": "Схоже, що ви вручну відредагували конфігурацію slapd. Для цього критичного переходу YunoHost повинен примусово оновити конфігурацію slapd. Оригінальні файли будуть збережені в {conf_backup_folder}.", + "migration_0019_add_new_attributes_in_ldap": "Додавання нових атрибутів для дозволів в базі даних LDAP", + "migration_0018_failed_to_reset_legacy_rules": "Не вдалося скинути застарілі правила iptables: {error}", + "migration_0018_failed_to_migrate_iptables_rules": "Не вдалося перенести застарілі правила iptables в nftables: {error}", + "migration_0017_not_enough_space": "Звільніть достатньо місця в {path} для запуску міграції.", + "migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 встановлений, але не postgresql 11‽ Можливо, у вашій системі відбулося щось дивне: (...", + "migration_0017_postgresql_96_not_installed": "PostgreSQL ні встановлено у вашій системі. Нічого не потрібно робити.", + "migration_0015_weak_certs": "Було виявлено, що такі сертифікати все ще використовують слабкі алгоритми підпису і повинні бути оновлені для сумісності з наступною версією nginx: {certs}", + "migration_0015_cleaning_up": "Очищення кеш-пам'яті і пакетів, які більше не потрібні...", + "migration_0015_specific_upgrade": "Початок поновлення системних пакетів, які повинні бути оновлені незалежно...", + "migration_0015_modified_files": "Зверніть увагу, що такі файли були змінені вручну і можуть бути перезаписані після поновлення: {manually_modified_files}.", + "migration_0015_problematic_apps_warning": "Зверніть увагу, що були виявлені наступні, можливо, проблемні встановлені додатки. Схоже, що вони не були встановлені з каталогу додатків YunoHost або не зазначені як \"робочі\". Отже, не можна гарантувати, що вони будуть працювати після оновлення: {problematic_apps}.", + "migration_0015_general_warning": "Будь ласка, зверніть увагу, що ця міграція є делікатною операцією. Команда YunoHost зробила все можливе, щоб перевірити і протестувати її, але міграція все ще може порушити частина системи або її додатків. Тому рекомендується: - Виконати резервне копіювання всіх важливих даних або додатків. Більш детальна інформація на сайті https://yunohost.org/backup; - Наберіться терпіння після запуску міграції: В залежності від вашого підключення до Інтернету і апаратного забезпечення, оновлення може зайняти до декількох годин.", + "migration_0015_system_not_fully_up_to_date": "Ваша система не повністю оновлена. Будь ласка, виконайте регулярне оновлення перед запуском міграції на Buster.", + "migration_0015_not_enough_free_space": "Вільного місця в/var/досить мало! У вас повинно бути не менше 1 ГБ вільного місця, щоб запустити цю міграцію.", + "migration_0015_not_stretch": "Поточний дистрибутив Debian не є Stretch!", + "migration_0015_yunohost_upgrade": "Починаємо оновлення ядра YunoHost...", + "migration_0015_still_on_stretch_after_main_upgrade": "Щось пішло не так під час основного поновлення, система, схоже, все ще знаходиться на Debian Stretch", + "migration_0015_main_upgrade": "Початок основного поновлення...", + "migration_0015_patching_sources_list": "Виправлення sources.lists...", + "migration_0015_start": "Початок міграції на Buster", + "migration_update_LDAP_schema": "Оновлення схеми LDAP...", + "migration_ldap_rollback_success": "Система відкотилася.", + "migration_ldap_migration_failed_trying_to_rollback": "Не вдалося виконати міграцію... спроба відкату системи.", + "migration_ldap_can_not_backup_before_migration": "Не вдалося завершити резервне копіювання системи перед невдалої міграцією. Помилка: {error}", + "migration_ldap_backup_before_migration": "Створення резервної копії бази даних LDAP і установки додатків перед фактичної міграцією.", + "migration_description_0020_ssh_sftp_permissions": "Додайте підтримку дозволів SSH і SFTP", + "migration_description_0019_extend_permissions_features": "Розширення/переробка системи управління дозволами додатків", + "migration_description_0018_xtable_to_nftable": "Перенесення старих правил мережевого трафіку в нову систему nftable", + "migration_description_0017_postgresql_9p6_to_11": "Перенесення баз даних з PostgreSQL 9.6 на 11", + "migration_description_0016_php70_to_php73_pools": "Перенесіть php7.0-fpm 'pool' conf файли на php7.3", + "migration_description_0015_migrate_to_buster": "Оновлення системи до Debian Buster і YunoHost 4.x", + "migrating_legacy_permission_settings": "Перенесення застарілих налаштувань дозволів...", + "main_domain_changed": "Основний домен був змінений", + "main_domain_change_failed": "Неможливо змінити основний домен", + "mail_unavailable": "Ця електронна адреса зарезервований і буде автоматично виділено найпершого користувачеві", + "mailbox_used_space_dovecot_down": "Поштова служба Dovecot повинна бути запущена, якщо ви хочете отримати використане місце в поштовій скриньці.", + "mailbox_disabled": "Електронна пошта відключена для користувача {user}", + "mail_forward_remove_failed": "Не вдалося видалити переадресацію електронної пошти '{mail}'", + "mail_domain_unknown": "Неправильну адресу електронної пошти для домену '{domain}'. Будь ласка, використовуйте домен, адмініструється цим сервером.", + "mail_alias_remove_failed": "Не вдалося видалити псевдонім електронної пошти '{mail}'", + "log_tools_reboot": "перезавантажити сервер", + "log_tools_shutdown": "Вимкнути ваш сервер", + "log_tools_upgrade": "Оновлення системних пакетів", + "log_tools_postinstall": "Постінсталляція вашого сервера YunoHost", + "log_tools_migrations_migrate_forward": "запустіть міграції", + "log_domain_main_domain": "Зробити '{}' основним доменом", + "log_user_permission_reset": "Скинути дозвіл \"{} '", + "log_user_permission_update": "Оновити доступи для дозволу '{}'", + "log_user_update": "Оновити інформацію для користувача '{}'", + "log_user_group_update": "Оновити групу '{}'", + "log_user_group_delete": "Видалити групу \"{} '", + "log_user_group_create": "Створити групу '{}'", + "log_user_delete": "Видалити користувача '{}'", + "log_user_create": "Додати користувача '{}'", + "log_regen_conf": "Регенерувати системні конфігурації '{}'", + "log_letsencrypt_cert_renew": "Оновити сертифікат Let's Encrypt на домені '{}'", + "log_selfsigned_cert_install": "Встановити самоподпісанний сертифікат на домені '{}'", + "log_permission_url": "Оновити URL, пов'язаний з дозволом '{}'", + "log_permission_delete": "Видалити дозвіл \"{} '", + "log_permission_create": "Створити дозвіл \"{} '", + "log_letsencrypt_cert_install": "Встановіть сертифікат Let's Encrypt на домен '{}'", + "log_dyndns_update": "Оновити IP, пов'язаний з вашим піддоменом YunoHost '{}'", + "log_dyndns_subscribe": "Підписка на піддомен YunoHost '{}'", + "log_domain_remove": "Видалити домен '{}' з конфігурації системи", + "log_domain_add": "Додати домен '{}' в конфігурацію системи", + "log_remove_on_failed_install": "Видалити '{}' після невдалої установки", + "log_remove_on_failed_restore": "Видалити '{}' після невдалого відновлення з резервного архіву", + "log_backup_restore_app": "Відновлення '{}' з архіву резервних копій", + "log_backup_restore_system": "Відновлення системи з резервного архіву", + "log_backup_create": "Створення резервного архіву", + "log_available_on_yunopaste": "Цей журнал тепер доступний за посиланням {url}", + "log_app_config_apply": "Застосувати конфігурацію до додатка \"{} '", + "log_app_config_show_panel": "Показати панель конфігурації програми \"{} '", + "log_app_action_run": "Активації дії додатка \"{} '", + "log_app_makedefault": "Зробити '{}' додатком за замовчуванням", + "log_app_upgrade": "Оновити додаток '{}'", + "log_app_remove": "Для видалення програми '{}'", + "log_app_install": "Встановіть додаток '{}'", + "log_app_change_url": "Змініть URL-адресу додатка \"{} '", + "log_operation_unit_unclosed_properly": "Блок операцій не був закритий належним чином", + "log_does_exists": "Немає журналу операцій з ім'ям '{log}', використовуйте 'yunohost log list', щоб подивитися всі публічні журнали операцій", + "log_help_to_get_failed_log": "Операція '{desc}' не може бути завершена. Будь ласка, поділіться повним журналом цієї операції, використовуючи команду 'yunohost log share {name}', щоб отримати допомогу.", + "log_link_to_failed_log": "Не вдалося завершити операцію '{desc}'. Будь ласка, надайте повний журнал цієї операції, натиснувши тут , щоб отримати допомогу.", + "log_help_to_get_log": "Щоб переглянути журнал операції '{desc}', використовуйте команду 'yunohost log show {name} {name}'.", + "log_link_to_log": "Повний журнал цієї операції: ' {desc} '", + "log_corrupted_md_file": "Файл метаданих YAML, пов'язаний з журналами, пошкоджений: '{md_file} Помилка: {error}'", + "iptables_unavailable": "Ви не можете грати з iptables тут. Ви перебуваєте або в контейнері, або ваше ядро не підтримує його.", + "ip6tables_unavailable": "Ви не можете грати з ip6tables тут. Ви перебуваєте або в контейнері, або ваше ядро не підтримує його.", + "invalid_regex": "Невірний regex: '{regex}'", + "installation_complete": "установка завершена", + "hook_name_unknown": "Невідоме ім'я хука '{name}'", + "hook_list_by_invalid": "Це властивість не може бути використано для перерахування хуков", + "hook_json_return_error": "Не вдалося розпізнати повернення з хука {шлях}. Помилка: {msg}. Необроблений контент: {raw_content}.", + "hook_exec_not_terminated": "Скрипт не завершився належним чином: {шлях}", + "hook_exec_failed": "Не вдалося запустити скрипт: {path}", + "group_user_not_in_group": "Користувач {user} не входить в групу {group}", + "group_user_already_in_group": "Користувач {user} вже в групі {group}", + "group_update_failed": "Не вдалося оновити групу '{group}': {error}", + "group_updated": "Група '{group}' оновлена", + "group_unknown": "Група '{group}' невідома.", + "group_deletion_failed": "Не вдалося видалити групу '{group}': {error}", + "group_deleted": "Група '{group}' вилучена", + "group_cannot_be_deleted": "Група {group} не може бути видалена вручну.", + "group_cannot_edit_primary_group": "Група '{group}' не може бути відредаговано вручну. Це основна група, призначена тільки для одного конкретного користувача.", + "group_cannot_edit_visitors": "Група 'visitors' не може бути відредаговано вручну. Це спеціальна група, що представляє анонімних відвідувачів.", + "group_cannot_edit_all_users": "Група 'all_users' не може бути відредаговано вручну. Це спеціальна група, призначена для всіх користувачів, зареєстрованих в YunoHost.", + "group_creation_failed": "Не вдалося створити групу \"{group} ': {error}", + "group_created": "Група '{group}' створена", + "group_already_exist_on_system_but_removing_it": "Група {group} вже існує в групах системи, але YunoHost видалить її...", + "group_already_exist_on_system": "Група {group} вже існує в групах системи", + "group_already_exist": "Група {group} вже існує", + "good_practices_about_user_password": "Зараз ви маєте визначити новий пароль користувача. Пароль повинен складатися не менше ніж з 8 символів, але хорошою практикою є використання більш довгого пароля (тобто парольної фрази) і/або використання різних символів (заголовних, малих, цифр і спеціальних символів).", + "good_practices_about_admin_password": "Зараз ви збираєтеся поставити новий пароль адміністратора. Пароль повинен складатися не менше ніж з 8 символів, але хорошою практикою є використання більш довгого пароля (тобто парольної фрази) і/або використання різних символів (прописних, малих, цифр і спеціальних символів).", + "global_settings_unknown_type": "Несподівана ситуація, параметр {setting} має тип {unknown_type}, але це не тип, підтримуваний системою.", + "global_settings_setting_backup_compress_tar_archives": "При створенні нових резервних копій стискати архіви (.tar.gz) замість незжатих архівів (.tar). NB: включення цієї опції означає створення більш легких архівів резервних копій, але початкова процедура резервного копіювання буде значно довше і важче для CPU.", + "global_settings_setting_security_webadmin_allowlist": "IP-адреси, яким дозволений доступ до веб-адміну. Через кому.", + "global_settings_setting_security_webadmin_allowlist_enabled": "Дозволити доступ до веб-адміну тільки деяким IP-адресами.", + "global_settings_setting_smtp_relay_password": "Пароль хоста SMTP-ретрансляції", + "global_settings_setting_smtp_relay_user": "Обліковий запис користувача SMTP-реле", + "global_settings_setting_smtp_relay_port": "Порт SMTP-реле", + "global_settings_setting_smtp_relay_host": "SMTP релейний хост, який буде використовуватися для відправки пошти замість цього примірника yunohost. Корисно, якщо ви знаходитеся в одній із цих ситуацій: ваш 25 порт заблокований вашим провайдером або VPS провайдером, у вас є житловий IP в списку DUHL, ви не можете налаштувати зворотний DNS або цей сервер не доступний безпосередньо в інтернеті і ви хочете використовувати інший сервер для відправки пошти.", + "global_settings_setting_smtp_allow_ipv6": "Дозволити використання IPv6 для отримання і відправки пошти", + "global_settings_setting_ssowat_panel_overlay_enabled": "Включити накладення панелі SSOwat", + "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Дозволити використання (застарілого) ключа DSA для конфігурації демона SSH", + "global_settings_unknown_setting_from_settings_file": "Невідомий ключ в настройках: '{setting_key}', відкиньте його і збережіть в /etc/yunohost/settings-unknown.json", + "global_settings_setting_security_ssh_port": "SSH-порт", + "global_settings_setting_security_postfix_compatibility": "Компроміс між сумісністю і безпекою для сервера Postfix. Впливає на шифри (і інші аспекти, пов'язані з безпекою)", + "global_settings_setting_security_ssh_compatibility": "Сумісність і співвідношення безпеки для SSH-сервера. Впливає на шифри (і інші аспекти, пов'язані з безпекою)", + "global_settings_setting_security_password_user_strength": "Надійність пароля користувача", + "global_settings_setting_security_password_admin_strength": "Надійність пароля адміністратора", + "global_settings_setting_security_nginx_compatibility": "Компроміс між сумісністю і безпекою для веб-сервера NGINX. Впливає на шифри (і інші аспекти, пов'язані з безпекою)", + "global_settings_setting_pop3_enabled": "Включити протокол POP3 для поштового сервера.", + "global_settings_reset_success": "Попередні настройки тепер збережені в {шлях}.", + "global_settings_key_doesnt_exists": "Ключ '{settings_key}' не існує в глобальних налаштуваннях, ви можете побачити всі доступні ключі, виконавши команду 'yunohost settings list'.", + "global_settings_cant_write_settings": "Неможливо зберегти файл настройок, причина: {причина}", + "global_settings_cant_serialize_settings": "Не вдалося серіалізовать дані налаштувань, причина: {причина}", + "global_settings_cant_open_settings": "Не вдалося відкрити файл настройок, причина: {причина}", + "global_settings_bad_type_for_setting": "Поганий тип для настройки {setting}, отриманий {received_type}, очікується {expected_type}", + "global_settings_bad_choice_for_enum": "Поганий вибір для настройки {setting}, отримано '{choice}', але доступні наступні варіанти: {available_choices}.", + "firewall_rules_cmd_failed": "Деякі команди правил брандмауера не спрацювали. Більш детальна інформація в журналі.", + "firewall_reloaded": "брандмауер перезавантажений", + "firewall_reload_failed": "Не вдалося перезавантажити брандмауер", + "file_does_not_exist": "Файл {шлях} не існує.", + "field_invalid": "Неприпустиме поле '{}'", + "experimental_feature": "Попередження: Ця функція є експериментальною і не вважається стабільною, ви не повинні використовувати її, якщо не знаєте, що робите.", + "extracting": "Витяг...", + "dyndns_unavailable": "Домен '{domain}' недоступний.", + "dyndns_domain_not_provided": "DynDNS провайдер {provider} не може надати домен {domain}.", + "dyndns_registration_failed": "Не вдалося зареєструвати домен DynDNS: {error}.", + "dyndns_registered": "Домен DynDNS зареєстрований", + "dyndns_provider_unreachable": "Неможливо зв'язатися з провайдером DynDNS {provider}: або ваш YunoHost неправильно підключений до інтернету, або сервер dynette не працює.", + "dyndns_no_domain_registered": "Домен не зареєстрований в DynDNS", + "dyndns_key_not_found": "DNS-ключ не знайдений для домену", + "dyndns_key_generating": "Генерація DNS-ключа... Це може зайняти деякий час.", + "dyndns_ip_updated": "Оновлення свій IP-адресу в DynDNS", + "dyndns_ip_update_failed": "Не вдалося оновити IP-адреса в DynDNS", + "dyndns_could_not_check_available": "Не вдалося перевірити наявність певної {домен} на {провайдера}.", + "dyndns_could_not_check_provide": "Не вдалося перевірити, чи може {провайдер} надати {домен}.", + "dpkg_lock_not_available": "Ця команда не може бути виконана прямо зараз, тому що інша програма, схоже, використовує блокування dpkg (системного менеджера пакетів).", + "dpkg_is_broken": "Ви не можете зробити це прямо зараз, тому що dpkg/APT (системні менеджери пакетів), схоже, знаходяться в зламаному стані... Ви можете спробувати вирішити цю проблему, підключившись через SSH і виконавши `sudo apt install --fix-broken` і/або `sudo dpkg --configure -a`.", + "downloading": "Завантаження…", + "done": "Готово", + "domains_available": "Доступні домени:", + "domain_unknown": "невідомий домен", + "domain_name_unknown": "Домен '{domain}' невідомий", + "domain_uninstall_app_first": "Ці додатки все ще встановлені на вашому домені: {apps} ласка, видаліть їх за допомогою 'yunohost app remove the_app_id' або перемістити їх на інший домен за допомогою 'yunohost app change-url the_app_id', перш ніж приступити до видалення домену.", + "domain_remove_confirm_apps_removal": "Видалення цього домену призведе до видалення цих додатків: {apps} Ви впевнені, що хочете це зробити? [{Відповіді}].", + "domain_hostname_failed": "Неможливо встановити нове ім'я хоста. Це може викликати проблеми в подальшому (можливо, все буде в порядку).", + "domain_exists": "Домен вже існує", + "domain_dyndns_root_unknown": "Невідомий кореневої домен DynDNS", + "domain_dyndns_already_subscribed": "Ви вже підписалися на домен DynDNS", + "domain_dns_conf_is_just_a_recommendation": "Ця команда показує * рекомендовану * конфігурацію. Насправді вона не встановлює конфігурацію DNS для вас. Ви самі повинні налаштувати свою зону DNS у реєстратора відповідно до цих рекомендацій.", + "domain_deletion_failed": "Неможливо видалити домен {domain}: {помилка}", + "domain_deleted": "домен видалений", + "domain_creation_failed": "Неможливо створити домен {domain}: {помилка}", + "domain_created": "домен створений", + "domain_cert_gen_failed": "Не вдалося згенерувати сертифікат", + "domain_cannot_remove_main_add_new_one": "Ви не можете видалити '{domain}', так як це основний домен і ваш єдиний домен, вам потрібно спочатку додати інший домен за допомогою 'yunohost domain add ', потім встановити його як основний домен за допомогою ' yunohost domain main-domain -n 'і потім ви можете видалити домен' {domain} 'за допомогою' yunohost domain remove {domain} ''.", + "domain_cannot_add_xmpp_upload": "Ви не можете додавати домени, що починаються з 'xmpp-upload.'. Таке ім'я зарезервовано для функції XMPP upload, вбудованої в YunoHost.", + "domain_cannot_remove_main": "Ви не можете видалити '{domain}', так як це основний домен, спочатку вам потрібно встановити інший домен в якості основного за допомогою 'yunohost domain main-domain -n '; ось список доменів-кандидатів: {other_domains}", + "disk_space_not_sufficient_update": "Недостатньо місця на диску для поновлення цього додатка", + "disk_space_not_sufficient_install": "Бракує місця на диску для установки цього додатка", + "diagnosis_sshd_config_inconsistent_details": "Будь ласка, виконайте yunohost settings set security.ssh.port -v YOUR_SSH_PORT , щоб визначити порт SSH, і перевірте yunohost tools regen-conf ssh --dry-run --with-diff yunohost tools regen-conf ssh --force , щоб скинути ваш conf на рекомендований YunoHost.", + "diagnosis_sshd_config_inconsistent": "Схоже, що порт SSH був вручну змінений в/etc/ssh/sshd_config. Починаючи з версії YunoHost 4.2, доступний новий глобальний параметр 'security.ssh.port', що дозволяє уникнути ручного редагування конфігурації.", + "diagnosis_sshd_config_insecure": "Схоже, що конфігурація SSH була змінена вручну і є небезпечною, оскільки не містить директив 'AllowGroups' або 'AllowUsers' для обмеження доступу авторизованих користувачів.", + "diagnosis_processes_killed_by_oom_reaper": "Деякі процеси були недавно вбито системою через брак пам'яті. Зазвичай це є симптомом нестачі пам'яті в системі або процесу, який з'їв дуже багато пам'яті. Зведення убитих процесів: {kills_summary}", + "diagnosis_never_ran_yet": "Схоже, що цей сервер був налаштований недавно, і поки немає звіту про діагностику. Вам слід почати з повної діагностики, або з веб-адміністратора, або використовуючи 'yunohost diagnosis run' з командного рядка.", + "diagnosis_unknown_categories": "Наступні категорії невідомі: {categories}", + "diagnosis_http_nginx_conf_not_up_to_date_details": "Щоб виправити ситуацію, перевірте різницю за допомогою командного рядка, використовуючи yunohost tools regen-conf nginx --dry-run --with-diff , і якщо все в порядку, застосуйте зміни за допомогою yunohost tools regen-conf nginx --force .", + "diagnosis_http_nginx_conf_not_up_to_date": "Схоже, що конфігурація nginx цього домену була змінена вручну, що не дозволяє YunoHost визначити, чи доступний він по HTTP.", + "diagnosis_http_partially_unreachable": "Домен {domain} здається недоступним по HTTP ззовні локальної мережі в IPv {failed}, хоча він працює в IPv {passed}.", + "diagnosis_http_unreachable": "Домен {domain} здається недоступним через HTTP ззовні локальної мережі.", + "diagnosis_http_bad_status_code": "Схоже, що замість вашого сервера відповіла інша машина (можливо, ваш маршрутизатор).
1. Найбільш поширеною причиною цієї проблеми є те, що порт 80 (і 443) неправильно перенаправлений на ваш сервер .
2. На більш складних установках: переконайтеся, що немає брандмауера або зворотного проксі.", + "diagnosis_http_connection_error": "Помилка підключення: не вдалося підключитися до запитуваного домену, швидше за все, він недоступний.", + "diagnosis_http_timeout": "При спробі зв'язатися з вашим сервером ззовні стався тайм-аут. Він здається недоступним.
1. Найбільш поширеною причиною цієї проблеми є те, що порт 80 (і 443) неправильно перенаправлений на ваш сервер .
2. Ви також повинні переконатися, що служба nginx запущена
3. На більш складних установках: переконайтеся, що немає брандмауера або зворотного проксі.", + "diagnosis_http_ok": "Домен {domain} доступний по HTTP ззовні локальної мережі.", + "diagnosis_http_localdomain": "Домен {domain} з доменом .local TLD не може бути доступний ззовні локальної мережі.", + "diagnosis_http_could_not_diagnose_details": "Помилка: {error}.", + "diagnosis_http_could_not_diagnose": "Не вдалося діагностувати досяжність доменів ззовні в IPv {ipversion}.", + "diagnosis_http_hairpinning_issue_details": "Можливо, це пов'язано з блоком/маршрутизатором вашого інтернет-провайдера. В результаті, люди ззовні вашої локальної мережі зможуть отримати доступ до вашого сервера, як і очікувалося, але не люди зсередини локальної мережі (як ви, ймовірно?) При використанні доменного імені або глобального IP. Можливо, ви зможете поліпшити ситуацію, глянувши на https://yunohost.org/dns_local_network .", + "diagnosis_http_hairpinning_issue": "Схоже, що у вашій локальній мережі не включена проброска.", + "diagnosis_ports_forwarding_tip": "Щоб вирішити цю проблему, вам, швидше за все, потрібно налаштувати кидок портів на вашому інтернет-маршрутизатор, як описано в https://yunohost.org/isp_box_config.", + "diagnosis_ports_needed_by": "Відкриття цього порту необхідно для функцій {категорії} (служба {сервіс}).", + "diagnosis_ports_ok": "Порт {port} доступний ззовні.", + "diagnosis_ports_partially_unreachable": "Порт {port} не доступний ззовні в IPv {failed}.", + "diagnosis_ports_unreachable": "Порт {port} недоступний ззовні.", + "diagnosis_ports_could_not_diagnose_details": "Помилка: {error}", + "diagnosis_ports_could_not_diagnose": "Не вдалося діагностувати досяжність портів ззовні в IPv {ipversion}.", + "diagnosis_description_regenconf": "Конфігурації системи", + "diagnosis_description_mail": "Електронна пошта", + "diagnosis_description_ports": "виявлення портів", + "diagnosis_description_systemresources": "Системні ресурси", + "diagnosis_description_services": "Перевірка стану служб", + "diagnosis_description_dnsrecords": "записи DNS", + "diagnosis_description_ip": "Інтернет-з'єднання", + "diagnosis_description_basesystem": "Базова система", + "diagnosis_security_vulnerable_to_meltdown_details": "Щоб виправити це, вам слід оновити систему і перезавантажитися, щоб завантажити нове ядро linux (або звернутися до вашого серверного провайдеру, якщо це не спрацює). Додаткову інформацію див. На сайті https://meltdownattack.com/.", + "diagnosis_security_vulnerable_to_meltdown": "Схоже, що ви уразливі до критичної уразливості безпеки Meltdown.", + "diagnosis_rootfstotalspace_critical": "Коренева файлова система має тільки {простір}, що вельми тривожно! Швидше за все, дисковий простір закінчиться дуже швидко! Рекомендується мати не менше 16 ГБ для кореневої файлової системи.", + "diagnosis_rootfstotalspace_warning": "Коренева файлова система має тільки {простір}. Це може бути нормально, але будьте обережні, тому що в кінцевому підсумку дисковий простір може швидко закінчитися... Рекомендується мати не менше 16 ГБ для кореневої файлової системи.", + "diagnosis_regenconf_manually_modified_details": "Це можливо нормально, якщо ви знаєте, що робите! YunoHost перестане оновлювати цей файл автоматично... Але врахуйте, що поновлення YunoHost можуть містити важливі рекомендовані зміни. Якщо ви хочете, ви можете перевірити відмінності за допомогою команди yunohost tools regen-conf {category} --dry-run --with-diff і примусово повернути рекомендовану конфігурацію за допомогою yunohost tools regen- conf {category} --force .", + "diagnosis_regenconf_manually_modified": "Конфігураційний файл {file} , схоже, був змінений вручну.", + "diagnosis_regenconf_allgood": "Всі конфігураційні файли відповідають рекомендованої конфігурації!", + "diagnosis_mail_queue_too_big": "Занадто багато відкладених листів в поштовій черзі ({nb_pending} emails)", + "diagnosis_mail_queue_unavailable_details": "Помилка: {error}", + "diagnosis_mail_queue_unavailable": "Неможливо дізнатися кількість очікують листів в черзі", + "diagnosis_mail_queue_ok": "{nb_pending} відкладені листи в поштових чергах", + "diagnosis_mail_blacklist_website": "Після визначення причини, по якій ви потрапили в чорний список, і її усунення, ви можете попросити видалити ваш IP або домен на {blacklist_website}.", + "diagnosis_mail_blacklist_reason": "Причина внесення в чорний список: {причина}", + "diagnosis_mail_blacklist_listed_by": "Ваш IP або домен {item} знаходиться в чорному списку {blacklist_name}.", + "diagnosis_mail_blacklist_ok": "IP-адреси і домени, які використовуються цим сервером, не внесені в чорний список", + "diagnosis_mail_fcrdns_different_from_ehlo_domain_details": "Поточний зворотний DNS: {rdns_domain}
Очікуване значення: {ehlo_domain} .", + "diagnosis_mail_fcrdns_different_from_ehlo_domain": "Зворотний DNS неправильно налаштований в IPv {ipversion}. Деякі електронні листи можуть бути не доставлені або можуть бути відзначені як спам.", + "diagnosis_mail_fcrdns_nok_alternatives_6": "Деякі провайдери не дозволяють вам налаштувати зворотний DNS (або їх функція може бути зламана...). Якщо ваш зворотний DNS правильно налаштований для IPv4, ви можете спробувати відключити використання IPv6 при відправці листів, виконавши команду yunohost settings set smtp.allow_ipv6 -v off . Примітка: останнє рішення означає, що ви не зможете відправляти або отримувати електронну пошту з нечисленних серверів, що використовують тільки IPv6.", + "diagnosis_mail_fcrdns_nok_alternatives_4": "Деякі провайдери не дозволять вам налаштувати зворотний DNS (або їх функція може бути зламана...). Якщо ви відчуваєте проблеми з-за цього, розгляньте наступні рішення:
- Деякі провайдери надають альтернативу
використання ретранслятора поштового сервера , хоча це має на увазі, що ретранслятор зможе шпигувати за вашим поштовим трафіком.
- Альтернативою для захисту конфіденційності є використання VPN * з виділеним публічним IP * для обходу подібних обмежень. Дивіться https://yunohost.org/#/vpn_advantage
- Або можна переключитися на іншого провайдера .", + "diagnosis_mail_fcrdns_nok_details": "Спочатку спробуйте налаштувати зворотний DNS з {ehlo_domain} в інтерфейсі вашого інтернет-маршрутизатора або в інтерфейсі вашого хостинг-провайдера. (Деякі хостинг-провайдери можуть вимагати, щоб ви відправили їм тікет підтримки для цього).", + "diagnosis_mail_fcrdns_dns_missing": "У IPv {ipversion} не визначений зворотний DNS. Деякі листи можуть не доставлятися або позначатися як спам.", + "diagnosis_mail_fcrdns_ok": "Ваш зворотний DNS налаштований правильно!", + "diagnosis_mail_ehlo_could_not_diagnose_details": "Помилка: {error}.", + "diagnosis_mail_ehlo_could_not_diagnose": "Не вдалося діагностувати наявність певної поштовий сервер postfix ззовні в IPv {ipversion}.", + "diagnosis_mail_ehlo_wrong_details": "EHLO, отриманий віддаленим діагностичним центром в IPv {ipversion}, відрізняється від домену вашого сервера.
Отриманий EHLO: {wrong_ehlo}
Очікуваний: {right_ehlo} < br> найпоширенішою причиною цієї проблеми є те, що порт 25 неправильно перенаправлений на ваш сервер . Крім того, переконайтеся, що в роботу сервера не втручається брандмауер або зворотний проксі-сервер.", + "diagnosis_mail_ehlo_wrong": "Інший поштовий SMTP-сервер відповідає на IPv {ipversion}. Ваш сервер, ймовірно, не зможе отримувати електронну пошту.", + "diagnosis_mail_ehlo_bad_answer_details": "Це може бути викликано тим, що замість вашого сервера відповідає інша машина.", + "diagnosis_mail_ehlo_bad_answer": "Ні-SMTP служба відповідає на порту 25 на IPv {ipversion}.", + "diagnosis_mail_ehlo_unreachable_details": "Не вдалося відкрити з'єднання по порту 25 з вашим сервером на IPv {ipversion}. Він здається недоступним.
1. Найбільш поширеною причиною цієї проблеми є те, що порт 25 неправильно перенаправлений на ваш сервер .
2. Ви також повинні переконатися, що служба postfix запущена.
3. На більш складних установках: переконайтеся, що немає брандмауера або зворотного проксі.", + "diagnosis_mail_ehlo_unreachable": "Поштовий сервер SMTP недоступний ззовні по IPv {ipversion}. Він не зможе отримувати повідомлення електронної пошти.", + "diagnosis_mail_ehlo_ok": "Поштовий сервер SMTP доступний ззовні і тому може отримувати електронну пошту!", + "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Деякі провайдери не дозволять вам розблокувати вихідний порт 25, тому що вони не піклуються про Net Neutrality.
- Деякі з них пропонують альтернативу використання ретранслятора поштового сервера , хоча це має на увазі, що ретранслятор зможе шпигувати за вашим поштовим трафіком.
- Альтернативою для захисту конфіденційності є використання VPN * з виділеним публічним IP * для обходу такого роду обмежень. Дивіться https://yunohost.org/#/vpn_advantage
- Ви також можете розглянути можливість переходу на более дружнього до мережевого нейтралітету провайдера .", + "diagnosis_mail_outgoing_port_25_blocked_details": "Спочатку спробуйте розблокувати вихідний порт 25 в інтерфейсі вашого інтернет-маршрутизатора або в інтерфейсі вашого хостинг-провайдера. (Деякі хостинг-провайдери можуть вимагати, щоб ви відправили їм заявку в службу підтримки).", + "diagnosis_mail_outgoing_port_25_blocked": "Поштовий сервер SMTP не може відправляти електронні листи на інші сервери, оскільки вихідний порт 25 заблокований в IPv {ipversion}.", + "app_manifest_install_ask_path": "Оберіть шлях URL (після домену), за яким має бути встановлено цей застосунок", + "yunohost_postinstall_end_tip": "Постінсталляція завершена! Щоб завершити установку, будь ласка, розгляньте наступні варіанти: - додавання першого користувача через розділ 'Користувачі' веб-адміністратора (або 'yunohost user create ' в командному рядку); - діагностику можливих проблем через розділ 'Діагностика' веб-адміністратора (або 'yunohost diagnosis run' в командному рядку); - читання розділів 'Завершення установки' і 'Знайомство з YunoHost' в документації адміністратора: https://yunohost.org/admindoc.", + "yunohost_not_installed": "YunoHost встановлений неправильно. Будь ласка, запустіть 'yunohost tools postinstall'.", + "yunohost_installing": "Установлення YunoHost...", + "yunohost_configured": "YunoHost вже налаштований", + "yunohost_already_installed": "YunoHost вже встановлено", + "user_updated": "Інформація про користувача змінена", + "user_update_failed": "Не вдалося оновити користувача {user}: {error}", + "user_unknown": "Невідомий користувач: {user}", + "user_home_creation_failed": "Не вдалося створити домашню папку для користувача", + "user_deletion_failed": "Не вдалося видалити користувача {user}: {error}", + "user_deleted": "користувача видалено", + "user_creation_failed": "Не вдалося створити користувача {user}: {помилка}", + "user_created": "Аккаунт було створено", + "user_already_exists": "Користувач '{user}' вже існує", + "upnp_port_open_failed": "Не вдалося відкрити порт через UPnP", + "upnp_enabled": "UPnP включено", + "upnp_disabled": "UPnP вимкнено", + "upnp_dev_not_found": "UPnP-пристрій, не знайдено", + "upgrading_packages": "Оновлення пакетів...", + "upgrade_complete": "оновлення завершено", + "updating_apt_cache": "Вибірка доступних оновлень для системних пакетів...", + "update_apt_cache_warning": "Щось пішло не так при оновленні кеша APT (менеджера пакунків Debian). Ось дамп рядків sources.list, який може допомогти визначити проблемні рядки: {sourceslist}", + "update_apt_cache_failed": "Неможливо оновити кеш APT (менеджер пакетів Debian). Ось дамп рядків sources.list, який може допомогти визначити проблемні рядки: {sourceslist}", + "unrestore_app": "{App} не буде поновлено", + "unlimit": "немає квоти", + "unknown_main_domain_path": "Невідомий домен або шлях для '{app}'. Вам необхідно вказати домен і шлях, щоб мати можливість вказати URL для дозволу.", + "unexpected_error": "Щось пішло не так: {error}", + "unbackup_app": "{App} НЕ буде збережений", + "tools_upgrade_special_packages_completed": "Оновлення пакета YunoHost завершено. Натисніть [Enter] для повернення командного рядка", + "tools_upgrade_special_packages_explanation": "Спеціальне оновлення триватиме у фоновому режимі. Будь ласка, не запускайте ніяких інших дій на вашому сервері протягом наступних ~ 10 хвилин (в залежності від швидкості обладнання). Після цього вам, можливо, доведеться заново увійти в веб-адмін. Журнал поновлення буде доступний в Інструменти → Журнал (в веб-адміном) або за допомогою 'yunohost log list' (з командного рядка).", + "tools_upgrade_special_packages": "Тепер оновлюємо \"спеціальні\" (пов'язані з yunohost) пакети…", + "tools_upgrade_regular_packages_failed": "Не вдалося оновити пакети: {packages_list}", + "tools_upgrade_regular_packages": "Тепер оновлюємо \"звичайні\" (не пов'язані з yunohost) пакети…", + "tools_upgrade_cant_unhold_critical_packages": "Не вдалося утримати критичні пакети…", + "tools_upgrade_cant_hold_critical_packages": "Не вдалося утримати критичні пакети…", + "tools_upgrade_cant_both": "Неможливо оновити систему і програми одночасно", + "tools_upgrade_at_least_one": "Будь ласка, вкажіть 'apps', або 'system'.", + "this_action_broke_dpkg": "Ця дія порушило dpkg/APT (системні менеджери пакетів)... Ви можете спробувати вирішити цю проблему, підключившись по SSH і запустивши `sudo apt install --fix-broken` і/або` sudo dpkg --configure -a`.", + "system_username_exists": "Ім'я користувача вже існує в списку користувачів системи", + "system_upgraded": "система оновлена", + "ssowat_conf_updated": "Конфігурація SSOwat оновлена", + "ssowat_conf_generated": "Регенерувати конфігурація SSOwat", + "show_tile_cant_be_enabled_for_regex": "Ви не можете включити 'show_tile' прямо зараз, тому що URL для дозволу '{permission}' являє собою регекс", + "show_tile_cant_be_enabled_for_url_not_defined": "Ви не можете включити 'show_tile' прямо зараз, тому що спочатку ви повинні визначити URL для дозволу '{permission}'", + "service_unknown": "Невідома служба '{service}'", + "service_stopped": "Служба '{service}' зупинена", + "service_stop_failed": "Неможливо зупинити службу '{service}' Недавні журнали служб: {logs}", + "service_started": "Служба '{service}' запущена", + "service_start_failed": "Не вдалося запустити службу '{service}' Recent service logs: {logs}", + "diagnosis_mail_outgoing_port_25_ok": "Поштовий сервер SMTP може відправляти електронні листи (вихідний порт 25 не заблокований).", + "diagnosis_swap_tip": "Будь ласка, будьте обережні і знайте, що якщо сервер розміщує своп на SD-карті або SSD-накопичувачі, це може різко скоротити термін служби устройства`.", + "diagnosis_swap_ok": "Система має {усього} свопу!", + "diagnosis_swap_notsomuch": "Система має тільки {усього} свопу. Щоб уникнути ситуацій, коли в системі закінчується пам'ять, слід передбачити наявність не менше {рекомендованого} обсягу підкачки.", + "diagnosis_swap_none": "В системі повністю відсутній своп. Ви повинні розглянути можливість додавання принаймні {рекомендованого} обсягу підкачки, щоб уникнути ситуацій, коли системі не вистачає пам'яті.", + "diagnosis_ram_ok": "Система все ще має {доступно} ({доступний_процент}%) оперативної пам'яті з {усього}.", + "diagnosis_ram_low": "У системі є {доступно} ({доступний_процент}%) оперативної пам'яті (з {усього}). Будьте уважні.", + "diagnosis_ram_verylow": "Система має тільки {доступне} ({доступний_процент}%) оперативної пам'яті! (З {усього})", + "diagnosis_diskusage_ok": "У сховищі {mountpoint} (на пристрої {device} ) залишилося {free} ({free_percent}%) вільного місця (з {total})!", + "diagnosis_diskusage_low": "Сховище {mountpoint} (на пристрої {device} ) має тільки {free} ({free_percent}%) вільного місця (з {total}). Будьте уважні.", + "diagnosis_diskusage_verylow": "Сховище {mountpoint} (на пристрої {device} ) має тільки {free} ({free_percent}%) вільного місця (з {total}). Вам дійсно варто подумати про очищення простору!", + "diagnosis_services_bad_status_tip": "Ви можете спробувати перезапустити службу , а якщо це не допоможе, подивіться журнали служби в webadmin (з командного рядка це можна зробити за допомогою yunohost service restart {service} yunohost service log {service} ).", + "diagnosis_services_bad_status": "Сервіс {service} знаходиться в {status} :(", + "diagnosis_services_conf_broken": "Конфігурація порушена для служби {service}!", + "diagnosis_services_running": "Служба {service} запущена!", + "diagnosis_domain_expires_in": "Термін дії {домену} закінчується через {днів} днів.", + "diagnosis_domain_expiration_error": "Термін дії деяких доменів закінчується ДУЖЕ СКОРО!", + "diagnosis_domain_expiration_warning": "Термін дії деяких доменів закінчиться найближчим часом!", + "diagnosis_domain_expiration_success": "Ваші домени зареєстровані і не збираються спливати найближчим часом.", + "diagnosis_domain_expiration_not_found_details": "Інформація WHOIS для домену {domain} не містить інформації про термін дії?", + "diagnosis_domain_not_found_details": "Домен {domain} не існує в базі даних WHOIS або термін його дії закінчився!", + "diagnosis_domain_expiration_not_found": "Неможливо перевірити термін дії деяких доменів", + "diagnosis_dns_specialusedomain": "Домен {domain} заснований на домені верхнього рівня спеціального призначення (TLD) і тому не очікується, що у нього будуть актуальні записи DNS.", + "diagnosis_dns_try_dyndns_update_force": "Конфігурація DNS цього домену повинна автоматично управлятися YunoHost. Якщо це не так, ви можете спробувати примусово оновити її за допомогою команди yunohost dyndns update --force .", + "diagnosis_dns_point_to_doc": "Якщо вам потрібна допомога з налаштування DNS-записів, зверніться до документації на сайті https://yunohost.org/dns_config .", + "diagnosis_dns_discrepancy": "Наступний запис DNS, схоже, не відповідає рекомендованої конфігурації:
Type: {type}
Name: {name}
Поточне значення: {current}
Очікуване значення: {value} ", + "diagnosis_dns_missing_record": "Згідно рекомендованої конфігурації DNS, ви повинні додати запис DNS з наступною інформацією.
Тип: {type}
Name: {name}
Value: < code> {value} .", + "diagnosis_dns_bad_conf": "Деякі DNS-записи відсутні або невірні для домену {домен} (категорія {категорія})", + "diagnosis_dns_good_conf": "DNS-записи правильно налаштовані для домену {домен} (категорія {категорія})", + "diagnosis_ip_weird_resolvconf_details": "Файл /etc/resolv.conf повинен бути симлінк на /etc/resolvconf/run/resolv.conf , що вказує на 127.0.0.1 (dnsmasq ). Якщо ви хочете вручну налаштувати DNS Резолвер, відредагуйте /etc/resolv.dnsmasq.conf .", + "diagnosis_ip_weird_resolvconf": "Дозвіл DNS, схоже, працює, але схоже, що ви використовуєте для користувача /etc/resolv.conf .", + "diagnosis_ip_broken_resolvconf": "Схоже, що дозвіл доменних імен на вашому сервері порушено, що пов'язано з тим, що /etc/resolv.conf не вказує на 127.0.0.1 .", + "diagnosis_ip_broken_dnsresolution": "Дозвіл доменних імен, схоже, з якоїсь причини не працює... Брандмауер блокує DNS-запити?", + "diagnosis_ip_dnsresolution_working": "Дозвіл доменних імен працює!", + "diagnosis_ip_not_connected_at_all": "Здається, що сервер взагалі не підключений до Інтернету !?", + "diagnosis_ip_local": "Локальний IP: {local} .", + "diagnosis_ip_global": "Глобальний IP: {global} ", + "diagnosis_ip_no_ipv6_tip": "Наявність працюючого IPv6 не є обов'язковим для роботи вашого сервера, але це краще для здоров'я Інтернету в цілому. IPv6 зазвичай автоматично налаштовується системою або вашим провайдером, якщо він доступний. В іншому випадку вам, можливо, доведеться налаштувати деякі речі вручну, як пояснюється в документації тут: https://yunohost.org/#/ipv6. Якщо ви не можете включити IPv6 або якщо це здається вам занадто технічним, ви також можете сміливо ігнорувати це попередження.", + "diagnosis_ip_no_ipv6": "Сервер не має працюючого IPv6.", + "diagnosis_ip_connected_ipv6": "Сервер підключений до Інтернету через IPv6!", + "diagnosis_ip_no_ipv4": "Сервер не має працюючого IPv4.", + "diagnosis_ip_connected_ipv4": "Сервер підключений до Інтернету через IPv4!", + "diagnosis_no_cache": "Для категорії \"{категорія} 'ще немає кеша діагнозів.", + "diagnosis_failed": "Не вдалося результат діагностики для категорії '{категорія}': {error}", + "diagnosis_everything_ok": "Все виглядає добре для {категорії}!", + "diagnosis_found_warnings": "Знайдено {попередження} пунктів, які можна поліпшити для {категорії}.", + "diagnosis_found_errors_and_warnings": "Знайдено {помилки} істотний (і) питання (и) (і {попередження} попередження (я)), що відносяться до {категорії}!", + "diagnosis_found_errors": "Знайдена {помилка} важлива проблема (і), пов'язана з {категорією}!", + "diagnosis_ignored_issues": "(+ {Nb_ignored} проігнорована проблема (проблеми))", + "diagnosis_cant_run_because_of_dep": "Неможливо запустити діагностику для {категорії}, поки є важливі проблеми, пов'язані з {глибиною}.", + "diagnosis_cache_still_valid": "(Кеш все ще дійсний для діагностики {категорії}. Повторна діагностика поки не проводиться!)", + "diagnosis_failed_for_category": "Не вдалося провести діагностику для категорії '{категорія}': {error}", + "diagnosis_display_tip": "Щоб побачити знайдені проблеми, ви можете перейти в розділ Diagnosis в веб-адміном або виконати команду 'yunohost diagnosis show --issues --human-readable' з командного рядка.", + "diagnosis_package_installed_from_sury_details": "Деякі пакети були ненавмисно встановлені з стороннього сховища під назвою Sury. Команда YunoHost поліпшила стратегію роботи з цими пакетами, але очікується, що в деяких системах, які встановили додатки PHP7.3 ще на Stretch, залишаться деякі невідповідності. Щоб виправити цю ситуацію, спробуйте виконати наступну команду: {cmd_to_fix} .", + "diagnosis_package_installed_from_sury": "Деякі системні пакети повинні бути знижені в статусі", + "diagnosis_backports_in_sources_list": "Схоже, що apt (менеджер пакетів) налаштований на використання сховища backports. Якщо ви не знаєте, що робите, ми настійно не рекомендуємо встановлювати пакети з backports, тому що це може привести до нестабільності або конфліктів у вашій системі.", + "diagnosis_basesystem_ynh_inconsistent_versions": "Ви використовуєте несумісні версії пакетів YunoHost... швидше за все, через невдалий або часткового оновлення.", + "diagnosis_basesystem_ynh_main_version": "Сервер працює під управлінням YunoHost {main_version} ({repo})", + "diagnosis_basesystem_ynh_single_version": "{Пакет} версія: {версія} ({repo})", + "diagnosis_basesystem_kernel": "Сервер працює під управлінням ядра Linux {kernel_version}", + "diagnosis_basesystem_host": "Сервер працює під управлінням Debian {debian_version}", + "diagnosis_basesystem_hardware_model": "Модель сервера - {model}", + "diagnosis_basesystem_hardware": "Архітектура апаратного забезпечення сервера - {virt} {arch}", + "custom_app_url_required": "Ви повинні надати URL для оновлення вашого призначеного для користувача додатки {app}.", + "confirm_app_install_thirdparty": "НЕБЕЗПЕЧНО! Ця програма не входить в каталог додатків YunoHost. Установлення сторонніх додатків може порушити цілісність і безпеку вашої системи. Вам не слід встановлювати його, якщо ви не знаєте, що робите. НІЯКОЇ ПІДТРИМКИ НЕ БУДЕ, якщо цей додаток не буде працювати або зламає вашу систему... Якщо ви все одно готові піти на такий ризик, введіть '{answers}'.", + "confirm_app_install_danger": "НЕБЕЗПЕЧНО! Відомо, що це додаток все ще експериментальне (якщо не сказати, що воно явно не працює)! Вам не слід встановлювати його, якщо ви не знаєте, що робите. Ніякої підтримки не буде надано, якщо цей додаток не буде працювати або зламає вашу систему... Якщо ви все одно готові ризикнути, введіть '{answers}'.", + "confirm_app_install_warning": "Попередження: Ця програма може працювати, але не дуже добре інтегровано в YunoHost. Деякі функції, такі як єдина реєстрація та резервне копіювання/відновлення, можуть бути недоступні. Все одно встановити? [{Відповіді}]. ", + "certmanager_unable_to_parse_self_CA_name": "Не вдалося розібрати ім'я самоподпісивающегося центру (файл: {file})", + "certmanager_self_ca_conf_file_not_found": "Не вдалося знайти файл конфігурації для самоподпісивающегося центру (файл: {file})", + "certmanager_no_cert_file": "Не вдалося розпізнати файл сертифіката для домену {domain} (файл: {file})", + "certmanager_hit_rate_limit": "Для цього набору доменів {domain} недавно було випущено дуже багато сертифікатів. Будь ласка, спробуйте ще раз пізніше. Див. Https://letsencrypt.org/docs/rate-limits/ для отримання більш докладної інформації.", + "certmanager_warning_subdomain_dns_record": "Піддомен '{subdomain} \"не дозволяється на той же IP-адресу, що і' {domain} '. Деякі функції будуть недоступні, поки ви не виправите це і не перегенеріруете сертифікат.", + "certmanager_domain_http_not_working": "Домен {domain}, схоже, не доступний через HTTP. Будь ласка, перевірте категорію 'Web' в діагностиці для отримання додаткової інформації. (Якщо ви знаєте, що робите, використовуйте '--no-checks', щоб відключити ці перевірки).", + "certmanager_domain_dns_ip_differs_from_public_ip": "DNS-записи для домену '{domain}' відрізняються від IP цього сервера. Будь ласка, перевірте категорію 'DNS-записи' (основні) в діагностиці для отримання додаткової інформації. Якщо ви недавно змінили запис A, будь ласка, зачекайте, поки вона пошириться (деякі програми перевірки поширення DNS доступні в Інтернеті). (Якщо ви знаєте, що робите, використовуйте '--no-checks', щоб відключити ці перевірки).", + "certmanager_domain_cert_not_selfsigned": "Сертифікат для домену {domain} не є самоподпісанного. Ви впевнені, що хочете замінити його? (Для цього використовуйте '--force').", + "certmanager_domain_not_diagnosed_yet": "Поки немає результатів діагностики для домену {domain}. Будь ласка, повторно проведіть діагностику для категорій 'DNS-записи' і 'Web' в розділі діагностики, щоб перевірити, чи готовий домен до Let's Encrypt. (Або, якщо ви знаєте, що робите, використовуйте '--no-checks', щоб відключити ці перевірки).", + "certmanager_certificate_fetching_or_enabling_failed": "Спроба використовувати новий сертифікат для {domain} не спрацювала...", + "certmanager_cert_signing_failed": "Не вдалося підписати новий сертифікат", + "certmanager_cert_renew_success": "Сертифікат Let's Encrypt оновлений для домену '{domain}'", + "certmanager_cert_install_success_selfsigned": "Самоподпісанний сертифікат тепер встановлений для домену '{domain}'", + "certmanager_cert_install_success": "Сертифікат Let's Encrypt тепер встановлений для домену '{domain}'", + "certmanager_cannot_read_cert": "Щось не так сталося при спробі відкрити поточний сертифікат для домену {domain} (файл: {файл}), причина: {причина}", + "certmanager_attempt_to_replace_valid_cert": "Ви намагаєтеся перезаписати хороший і дійсний сертифікат для домену {domain}! (Використовуйте --force для обходу)", + "certmanager_attempt_to_renew_valid_cert": "Термін дії сертифіката для домену '{domain} \"не закінчується! (Ви можете використовувати --force, якщо знаєте, що робите)", + "certmanager_attempt_to_renew_nonLE_cert": "Сертифікат для домену '{domain}' не випущено Let's Encrypt. Неможливо продовжити його автоматично!", + "certmanager_acme_not_configured_for_domain": "Завдання ACME не може бути запущена для {domain} прямо зараз, тому що в його nginx conf відсутній відповідний фрагмент коду... Будь ласка, переконайтеся, що конфігурація nginx оновлена за допомогою `yunohost tools regen-conf nginx --dry-run - with-diff`.", + "backup_with_no_restore_script_for_app": "{App} не має скрипта відновлення, ви не зможете автоматично відновити резервну копію цього додатка.", + "backup_with_no_backup_script_for_app": "Додаток '{app}' не має скрипта резервного копіювання. Ігнорування.", + "backup_unable_to_organize_files": "Неможливо використовувати швидкий метод для організації файлів в архіві", + "backup_system_part_failed": "Не вдалося створити резервну копію системної частини '{part}'.", + "backup_running_hooks": "Запуск гачків резервного копіювання...", + "backup_permission": "Дозвіл на резервне копіювання для {app}", + "backup_output_symlink_dir_broken": "Ваш архівний каталог '{path}' є непрацюючою симлінк. Можливо, ви забули перемонтувати або підключити носій, на який вона вказує.", + "backup_output_directory_required": "Ви повинні вказати вихідний каталог для резервного копіювання", + "backup_output_directory_not_empty": "Ви повинні вибрати порожній вихідний каталог", + "backup_output_directory_forbidden": "Виберіть інший вихідний каталог. Резервні копії не можуть бути створені в підкаталогах/bin,/boot,/dev,/etc,/lib,/root,/run,/sbin,/sys,/usr,/var або /home/yunohost.backup/archives.", + "backup_nothings_done": "нічого зберігати", + "backup_no_uncompress_archive_dir": "Немає такого каталогу нестислого архіву", + "backup_mount_archive_for_restore": "Підготовка архіву для відновлення...", + "backup_method_tar_finished": "Створено архів резервного копіювання TAR", + "backup_method_custom_finished": "Призначений для користувача метод резервного копіювання '{метод}' завершено", + "backup_method_copy_finished": "Резервне копіювання завершено", + "backup_hook_unknown": "Гачок резервного копіювання '{hook}' невідомий", + "backup_deleted": "Резервна копія видалена", + "backup_delete_error": "Не вдалося видалити '{path}'", + "backup_custom_mount_error": "Призначений для користувача метод резервного копіювання не зміг пройти етап 'монтування'", + "backup_custom_backup_error": "Призначений для користувача метод резервного копіювання не зміг пройти етап 'резервне копіювання'", + "backup_csv_creation_failed": "Не вдалося створити CSV-файл, необхідний для відновлення", + "backup_csv_addition_failed": "Не вдалося додати файли для резервного копіювання в CSV-файл", + "backup_creation_failed": "Не вдалося створити архів резервного копіювання", + "backup_create_size_estimation": "Архів буде містити близько {розмір} даних.", + "backup_created": "Резервна копія створена", + "backup_couldnt_bind": "Не вдалося зв'язати {src} з {dest}.", + "backup_copying_to_organize_the_archive": "Копіювання {size} MB для організації архіву", + "backup_cleaning_failed": "Не вдалося очистити тимчасову папку резервного копіювання", + "backup_cant_mount_uncompress_archive": "Не вдалося змонтувати нестислий архів як захищений від запису", + "backup_ask_for_copying_if_needed": "Чи хочете ви тимчасово виконати резервне копіювання з використанням {size} MB? (Цей спосіб використовується, оскільки деякі файли не можуть бути підготовлені більш ефективним методом).", + "backup_archive_writing_error": "Не вдалося додати файли '{source}' (названі в архіві '{dest}') для резервного копіювання в стислий архів '{archive}'.", + "backup_archive_system_part_not_available": "Системна частина '{part}' недоступна в цій резервної копії", + "backup_archive_corrupted": "Схоже, що архів резервної копії \"{archive} 'пошкоджений: {error}", + "backup_archive_cant_retrieve_info_json": "Не вдалося завантажити інформацію для архіву '{archive}'... info.json не може бути отриманий (або не є коректним json).", + "backup_archive_open_failed": "Не вдалося відкрити архів резервних копій", + "backup_archive_name_unknown": "Невідомий локальний архів резервного копіювання з ім'ям '{name}'", + "backup_archive_name_exists": "Архів резервного копіювання з таким ім'ям вже існує.", + "backup_archive_broken_link": "Не вдалося отримати доступ до архіву резервного копіювання (непрацююча посилання на {path})", + "backup_archive_app_not_found": "Не вдалося знайти {app} в архіві резервного копіювання", + "backup_applying_method_tar": "Створення резервного TAR-архіву...", + "backup_applying_method_custom": "Виклик для користувача методу резервного копіювання '{метод}'...", + "backup_applying_method_copy": "Копіювання всіх файлів в резервну копію...", + "backup_app_failed": "Не вдалося створити резервну копію {app}", + "backup_actually_backuping": "Створення резервного архіву з зібраних файлів...", + "backup_abstract_method": "Цей метод резервного копіювання ще не реалізований", + "ask_password": "пароль", + "ask_new_path": "Новий шлях", + "ask_new_domain": "новий домен", + "ask_new_admin_password": "Новий адміністративний пароль", + "ask_main_domain": "основний домен", + "ask_lastname": "Прізвище", + "ask_firstname": "ім'я", + "ask_user_domain": "Домен для адреси електронної пошти користувача і облікового запису XMPP", + "apps_catalog_update_success": "Каталог додатків був оновлений!", + "apps_catalog_obsolete_cache": "Кеш каталогу додатків порожній або застарів.", + "apps_catalog_failed_to_download": "Неможливо завантажити каталог додатків {apps_catalog}: {помилка}.", + "apps_catalog_updating": "Оновлення каталогу додатків…", + "apps_catalog_init_success": "Система каталогу додатків инициализирована!", + "apps_already_up_to_date": "Всі додатки вже оновлені", + "app_packaging_format_not_supported": "Ця програма не може бути встановлено, тому що формат його упаковки не підтримується вашою версією YunoHost. Можливо, вам слід оновити вашу систему.", + "app_upgraded": "{App} оновлено", + "app_upgrade_some_app_failed": "Деякі програми не можуть бути оновлені", + "app_upgrade_script_failed": "Сталася помилка в сценарії оновлення програми", + "app_upgrade_failed": "Не вдалося оновити {app}: {error}", + "app_upgrade_app_name": "Зараз оновлюємо {app}...", + "app_upgrade_several_apps": "Наступні додатки будуть оновлені: {apps}", + "app_unsupported_remote_type": "Для додатка використовується непідтримуваний віддалений тип.", + "app_unknown": "невідоме додаток", + "app_start_restore": "Відновлення {app}...", + "app_start_backup": "Збір файлів для резервного копіювання {app}...", + "app_start_remove": "Видалення {app}...", + "app_start_install": "Установлення {app}...", + "app_sources_fetch_failed": "Не вдалося вихідні файли, URL коректний?", + "app_restore_script_failed": "Сталася помилка всередині скрипта відновити оригінальну програму", + "app_restore_failed": "Не вдалося відновити {app}: {error}", + "app_remove_after_failed_install": "Видалення програми після збою установки...", + "app_requirements_unmeet": "Вимоги не виконані для {app}, пакет {pkgname} ({version}) повинен бути {spec}.", + "app_requirements_checking": "Перевірка необхідних пакетів для {app}...", + "app_removed": "{App} видалено", + "app_not_properly_removed": "{App} не було видалено належним чином", + "app_not_installed": "Не вдалося знайти {app} в списку встановлених додатків: {all_apps}", + "app_not_correctly_installed": "{App}, схоже, неправильно встановлено", + "app_not_upgraded": "Додаток '{failed_app}' не вдалося оновити, і, як наслідок, оновлення таких програмах було скасовано: {apps}", + "app_manifest_install_ask_is_public": "Чи повинно це додаток бути відкрито для анонімних відвідувачів?", + "app_manifest_install_ask_admin": "Виберіть користувача-адміністратора для цього додатка", + "app_manifest_install_ask_password": "Виберіть пароль адміністратора для цього додатка" } From c3fe22f3f0b334375020ef93bdf66f3909c95d35 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Fri, 27 Aug 2021 17:19:45 +0000 Subject: [PATCH 092/114] [CI] Remove stale translated strings --- locales/fr.json | 2 +- locales/gl.json | 2 +- locales/it.json | 2 +- locales/pt.json | 2 +- locales/uk.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 7719e1390..1cac6bbba 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -638,4 +638,4 @@ "global_settings_setting_security_webadmin_allowlist_enabled": "Autorisez seulement certaines IP à accéder à la page web du portail d'administration (webadmin).", "diagnosis_http_localdomain": "Le domaine {domain}, avec un TLD .local, ne devrait pas être atteint depuis l'extérieur du réseau local.", "diagnosis_dns_specialusedomain": "Le domaine {domain} est basé sur un domaine de premier niveau (TLD) à usage spécial et ne devrait donc pas avoir d'enregistrements DNS réels." -} +} \ No newline at end of file diff --git a/locales/gl.json b/locales/gl.json index 5c0bec604..c38d13ad1 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -523,4 +523,4 @@ "permission_deleted": "O permiso '{permission}' foi eliminado", "permission_cant_add_to_all_users": "O permiso {permission} non pode ser concecido a tódalas usuarias.", "permission_currently_allowed_for_all_users": "Este permiso está concedido actualmente a tódalas usuarias ademáis de a outros grupos. Probablemente queiras ben eliminar o permiso 'all_users' ou ben eliminar os outros grupos que teñen permiso." -} +} \ No newline at end of file diff --git a/locales/it.json b/locales/it.json index a9fb6f9c8..c38bae59d 100644 --- a/locales/it.json +++ b/locales/it.json @@ -635,4 +635,4 @@ "global_settings_setting_security_webadmin_allowlist_enabled": "Permetti solo ad alcuni IP di accedere al webadmin.", "disk_space_not_sufficient_update": "Non c'è abbastanza spazio libero per aggiornare questa applicazione", "disk_space_not_sufficient_install": "Non c'è abbastanza spazio libero per installare questa applicazione" -} +} \ No newline at end of file diff --git a/locales/pt.json b/locales/pt.json index ef0c41349..cc47da946 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -141,4 +141,4 @@ "app_install_failed": "Não foi possível instalar {app}: {error}", "app_full_domain_unavailable": "Desculpe, esse app deve ser instalado num domínio próprio mas já há outros apps instalados no domínio '{domain}'. Você pode usar um subdomínio dedicado a esse aplicativo.", "app_change_url_success": "A URL agora é {domain}{path}" -} +} \ No newline at end of file diff --git a/locales/uk.json b/locales/uk.json index c85b4fd0a..d51ec706c 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -33,4 +33,4 @@ "action_invalid": "Неприпустима дія '{action}'", "aborting": "Переривання.", "diagnosis_description_web": "Мережа" -} +} \ No newline at end of file From 712e2bb1c933550cb7f97d0afc79d779852094ab Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Fri, 27 Aug 2021 18:07:37 +0000 Subject: [PATCH 093/114] [CI] Format code --- src/yunohost/authenticators/ldap_admin.py | 6 ++- src/yunohost/tests/conftest.py | 3 +- .../tests/test_apps_arguments_parsing.py | 44 ++++++++++++++----- src/yunohost/tests/test_ldapauth.py | 1 + src/yunohost/utils/ldap.py | 5 +-- 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/yunohost/authenticators/ldap_admin.py b/src/yunohost/authenticators/ldap_admin.py index dd6eec03e..94d68a8db 100644 --- a/src/yunohost/authenticators/ldap_admin.py +++ b/src/yunohost/authenticators/ldap_admin.py @@ -12,6 +12,7 @@ from yunohost.utils.error import YunohostError logger = logging.getLogger("yunohost.authenticators.ldap_admin") + class Authenticator(BaseAuthenticator): name = "ldap_admin" @@ -57,7 +58,10 @@ class Authenticator(BaseAuthenticator): raise else: if who != self.admindn: - raise YunohostError(f"Not logged with the appropriate identity ? Found {who}, expected {self.admindn} !?", raw_msg=True) + raise YunohostError( + f"Not logged with the appropriate identity ? Found {who}, expected {self.admindn} !?", + raw_msg=True, + ) finally: # Free the connection, we don't really need it to keep it open as the point is only to check authentication... if con: diff --git a/src/yunohost/tests/conftest.py b/src/yunohost/tests/conftest.py index 9dfe2b39c..6b4e2c3fd 100644 --- a/src/yunohost/tests/conftest.py +++ b/src/yunohost/tests/conftest.py @@ -81,7 +81,8 @@ def pytest_cmdline_main(config): import yunohost yunohost.init(debug=config.option.yunodebug) - class DummyInterface(): + + class DummyInterface: type = "test" diff --git a/src/yunohost/tests/test_apps_arguments_parsing.py b/src/yunohost/tests/test_apps_arguments_parsing.py index 573c18cb2..fe5c5f8cd 100644 --- a/src/yunohost/tests/test_apps_arguments_parsing.py +++ b/src/yunohost/tests/test_apps_arguments_parsing.py @@ -180,7 +180,9 @@ def test_parse_args_in_yunohost_format_string_input_test_ask(): ] answers = {} - with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: + with patch.object( + Moulinette.interface, "prompt", return_value="some_value" + ) as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with(ask_text, False) @@ -197,7 +199,9 @@ def test_parse_args_in_yunohost_format_string_input_test_ask_with_default(): ] answers = {} - with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: + with patch.object( + Moulinette.interface, "prompt", return_value="some_value" + ) as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with("%s (default: %s)" % (ask_text, default_text), False) @@ -215,7 +219,9 @@ def test_parse_args_in_yunohost_format_string_input_test_ask_with_example(): ] answers = {} - with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: + with patch.object( + Moulinette.interface, "prompt", return_value="some_value" + ) as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert example_text in prompt.call_args[0][0] @@ -234,7 +240,9 @@ def test_parse_args_in_yunohost_format_string_input_test_ask_with_help(): ] answers = {} - with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: + with patch.object( + Moulinette.interface, "prompt", return_value="some_value" + ) as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert help_text in prompt.call_args[0][0] @@ -462,7 +470,9 @@ def test_parse_args_in_yunohost_format_password_input_test_ask(): ] answers = {} - with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: + with patch.object( + Moulinette.interface, "prompt", return_value="some_value" + ) as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with(ask_text, True) @@ -481,7 +491,9 @@ def test_parse_args_in_yunohost_format_password_input_test_ask_with_example(): ] answers = {} - with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: + with patch.object( + Moulinette.interface, "prompt", return_value="some_value" + ) as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert example_text in prompt.call_args[0][0] @@ -501,7 +513,9 @@ def test_parse_args_in_yunohost_format_password_input_test_ask_with_help(): ] answers = {} - with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: + with patch.object( + Moulinette.interface, "prompt", return_value="some_value" + ) as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert help_text in prompt.call_args[0][0] @@ -697,7 +711,9 @@ def test_parse_args_in_yunohost_format_path_input_test_ask(): ] answers = {} - with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: + with patch.object( + Moulinette.interface, "prompt", return_value="some_value" + ) as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with(ask_text, False) @@ -715,7 +731,9 @@ def test_parse_args_in_yunohost_format_path_input_test_ask_with_default(): ] answers = {} - with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: + with patch.object( + Moulinette.interface, "prompt", return_value="some_value" + ) as prompt: _parse_args_in_yunohost_format(answers, questions) prompt.assert_called_with("%s (default: %s)" % (ask_text, default_text), False) @@ -734,7 +752,9 @@ def test_parse_args_in_yunohost_format_path_input_test_ask_with_example(): ] answers = {} - with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: + with patch.object( + Moulinette.interface, "prompt", return_value="some_value" + ) as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert example_text in prompt.call_args[0][0] @@ -754,7 +774,9 @@ def test_parse_args_in_yunohost_format_path_input_test_ask_with_help(): ] answers = {} - with patch.object(Moulinette.interface, "prompt", return_value="some_value") as prompt: + with patch.object( + Moulinette.interface, "prompt", return_value="some_value" + ) as prompt: _parse_args_in_yunohost_format(answers, questions) assert ask_text in prompt.call_args[0][0] assert help_text in prompt.call_args[0][0] diff --git a/src/yunohost/tests/test_ldapauth.py b/src/yunohost/tests/test_ldapauth.py index 7560608f5..3a4e0dbb6 100644 --- a/src/yunohost/tests/test_ldapauth.py +++ b/src/yunohost/tests/test_ldapauth.py @@ -7,6 +7,7 @@ from yunohost.tools import tools_adminpw from moulinette import m18n from moulinette.core import MoulinetteError + def setup_function(function): if os.system("systemctl is-active slapd") != 0: diff --git a/src/yunohost/utils/ldap.py b/src/yunohost/utils/ldap.py index 1298eff69..4f571ce6f 100644 --- a/src/yunohost/utils/ldap.py +++ b/src/yunohost/utils/ldap.py @@ -56,7 +56,7 @@ def _get_ldap_interface(): def _ldap_path_extract(path, info): for element in path.split(","): if element.startswith(info + "="): - return element[len(info + "="):] + return element[len(info + "=") :] # Add this to properly close / delete the ldap interface / authenticator @@ -72,8 +72,7 @@ def _destroy_ldap_interface(): atexit.register(_destroy_ldap_interface) -class LDAPInterface(): - +class LDAPInterface: def __init__(self): logger.debug("initializing ldap interface") From 383aab55a66b3e94963f775e4e29134d22bbe25c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 28 Aug 2021 02:03:33 +0200 Subject: [PATCH 094/114] Fix tests --- locales/gl.json | 4 ++-- src/yunohost/tests/test_ldapauth.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/locales/gl.json b/locales/gl.json index c38d13ad1..5ccdad0c2 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -449,7 +449,7 @@ "migration_0019_slapd_config_will_be_overwritten": "Semella que editaches manualmente a configuración slapd. Para esta migración crítica YunoHost precisa forzar a actualización da configuración slapd. Os ficheiros orixinais van ser copiados en {conf_backup_folder}.", "migration_0019_add_new_attributes_in_ldap": "Engadir novos atributos para os permisos na base de datos LDAP", "migration_0018_failed_to_reset_legacy_rules": "Fallou o restablecemento das regras antigas de iptables: {error}", - "migration_0018_failed_to_migrate_iptables_rules": "Fallou a migración das regras antigas de iptables a nftables: {erro}", + "migration_0018_failed_to_migrate_iptables_rules": "Fallou a migración das regras antigas de iptables a nftables: {error}", "migration_0017_not_enough_space": "Crea espazo suficiente en {path} para executar a migración.", "migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 está instado, pero non postgresql 11? Algo raro debeu acontecer no teu sistema :(...", "migration_0017_postgresql_96_not_installed": "PostgreSQL non está instalado no teu sistema. Nada que facer.", @@ -523,4 +523,4 @@ "permission_deleted": "O permiso '{permission}' foi eliminado", "permission_cant_add_to_all_users": "O permiso {permission} non pode ser concecido a tódalas usuarias.", "permission_currently_allowed_for_all_users": "Este permiso está concedido actualmente a tódalas usuarias ademáis de a outros grupos. Probablemente queiras ben eliminar o permiso 'all_users' ou ben eliminar os outros grupos que teñen permiso." -} \ No newline at end of file +} diff --git a/src/yunohost/tests/test_ldapauth.py b/src/yunohost/tests/test_ldapauth.py index 3a4e0dbb6..a95dea443 100644 --- a/src/yunohost/tests/test_ldapauth.py +++ b/src/yunohost/tests/test_ldapauth.py @@ -24,7 +24,7 @@ def test_authenticate_with_wrong_password(): with pytest.raises(MoulinetteError) as exception: LDAPAuth().authenticate_credentials(credentials="bad_password_lul") - translation = m18n.g("invalid_password") + translation = m18n.n("invalid_password") expected_msg = translation.format() assert expected_msg in str(exception) @@ -52,7 +52,7 @@ def test_authenticate_change_password(): with pytest.raises(MoulinetteError) as exception: LDAPAuth().authenticate_credentials(credentials="yunohost") - translation = m18n.g("invalid_password") + translation = m18n.n("invalid_password") expected_msg = translation.format() assert expected_msg in str(exception) From ea76895fdfaa0006c5c6f6d8f67ce462cf5be368 Mon Sep 17 00:00:00 2001 From: Gregor Lenz Date: Sat, 28 Aug 2021 03:09:20 +0300 Subject: [PATCH 095/114] remove offensive language in package ban config (#1226) * remove offensive language in package ban config I understand that some users do silly things with their system configuration and this ban is supposed to prevent that. I just improved the wording for any user who like me stumbles across this file on their system... * [enh] Imrpove warnings * fix spelling mistakes Co-authored-by: ljf (zamentur) --- data/hooks/conf_regen/10-apt | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/data/hooks/conf_regen/10-apt b/data/hooks/conf_regen/10-apt index bb5caf67f..d2977ee92 100755 --- a/data/hooks/conf_regen/10-apt +++ b/data/hooks/conf_regen/10-apt @@ -17,19 +17,16 @@ Pin-Priority: -1" >> "${pending_dir}/etc/apt/preferences.d/extra_php_version" done echo " -# Yes ! -# This is what's preventing you from installing apache2 ! -# -# Maybe take two fucking minutes to realize that if you try to install -# apache2, this will break nginx and break the entire YunoHost ecosystem. -# on your server. -# -# So, *NO* -# DO NOT do this. -# DO NOT remove these lines. -# -# I warned you. I WARNED YOU! But did you listen to me? -# Oooooh, noooo. You knew it all, didn't you? + +# PLEASE READ THIS WARNING AND DON'T EDIT THIS FILE + +# You are probably reading this file because you tried to install apache2 or +# bind9. These 2 packages conflict with YunoHost. + +# Installing apache2 will break nginx and break the entire YunoHost ecosystem +# on your server, therefore don't remove those lines! + +# You have been warned. Package: apache2 Pin: release * @@ -39,9 +36,9 @@ Package: apache2-bin Pin: release * Pin-Priority: -1 -# Also yes, bind9 will conflict with dnsmasq. -# Same story than for apache2. -# Don't fucking install it. +# Also bind9 will conflict with dnsmasq. +# Same story as for apache2. +# Don't install it, don't remove those lines. Package: bind9 Pin: release * From d0cd3437ba6f1d47a388817398aab7e1a368f3a7 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Sat, 28 Aug 2021 00:45:56 +0000 Subject: [PATCH 096/114] [CI] Format code --- data/hooks/diagnosis/80-apps.py | 34 ++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/data/hooks/diagnosis/80-apps.py b/data/hooks/diagnosis/80-apps.py index 4ab5a6c0d..177ec590f 100644 --- a/data/hooks/diagnosis/80-apps.py +++ b/data/hooks/diagnosis/80-apps.py @@ -6,6 +6,7 @@ from yunohost.app import app_list from yunohost.diagnosis import Diagnoser + class AppDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] @@ -30,13 +31,17 @@ class AppDiagnoser(Diagnoser): if not app["issues"]: continue - level = "ERROR" if any(issue[0] == "error" for issue in app["issues"]) else "WARNING" + level = ( + "ERROR" + if any(issue[0] == "error" for issue in app["issues"]) + else "WARNING" + ) yield dict( meta={"test": "apps", "app": app["name"]}, status=level, summary="diagnosis_apps_issue", - details=[issue[1] for issue in app["issues"]] + details=[issue[1] for issue in app["issues"]], ) def issues(self, app): @@ -45,14 +50,19 @@ class AppDiagnoser(Diagnoser): if not app.get("from_catalog") or app["from_catalog"].get("state") != "working": yield ("error", "diagnosis_apps_not_in_app_catalog") - elif not isinstance(app["from_catalog"].get("level"), int) or app["from_catalog"]["level"] == 0: + elif ( + not isinstance(app["from_catalog"].get("level"), int) + or app["from_catalog"]["level"] == 0 + ): yield ("error", "diagnosis_apps_broken") elif app["from_catalog"]["level"] <= 4: yield ("warning", "diagnosis_apps_bad_quality") # Check for super old, deprecated practices - yunohost_version_req = app["manifest"].get("requirements", {}).get("yunohost", "").strip(">= ") + yunohost_version_req = ( + app["manifest"].get("requirements", {}).get("yunohost", "").strip(">= ") + ) if yunohost_version_req.startswith("2."): yield ("error", "diagnosis_apps_outdated_ynh_requirement") @@ -64,11 +74,21 @@ class AppDiagnoser(Diagnoser): "yunohost tools port-available", ] for deprecated_helper in deprecated_helpers: - if os.system(f"grep -nr -q '{deprecated_helper}' {app['setting_path']}/scripts/") == 0: + if ( + os.system( + f"grep -nr -q '{deprecated_helper}' {app['setting_path']}/scripts/" + ) + == 0 + ): yield ("error", "diagnosis_apps_deprecated_practices") - old_arg_regex = r'^domain=\${?[0-9]' - if os.system(f"grep -q '{old_arg_regex}' {app['setting_path']}/scripts/install") == 0: + old_arg_regex = r"^domain=\${?[0-9]" + if ( + os.system( + f"grep -q '{old_arg_regex}' {app['setting_path']}/scripts/install" + ) + == 0 + ): yield ("error", "diagnosis_apps_deprecated_practices") From a73b74a52db395a2f26fa4cb3a54042bb6da568a Mon Sep 17 00:00:00 2001 From: Weblate Date: Sat, 28 Aug 2021 21:46:54 +0000 Subject: [PATCH 097/114] Added translation using Weblate (Persian) --- locales/fa.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 locales/fa.json diff --git a/locales/fa.json b/locales/fa.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/locales/fa.json @@ -0,0 +1 @@ +{} From 27f6492f8b852bee40871ff810c4c49586aed1c4 Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 30 Aug 2021 14:14:32 +0000 Subject: [PATCH 098/114] Added translation using Weblate (Kurdish (Central)) --- locales/ckb.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 locales/ckb.json diff --git a/locales/ckb.json b/locales/ckb.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/locales/ckb.json @@ -0,0 +1 @@ +{} From 82bc5a93483e74bbd8ff78bf88e3dc4934bab00f Mon Sep 17 00:00:00 2001 From: Parviz Homayun Date: Tue, 31 Aug 2021 09:56:27 +0000 Subject: [PATCH 099/114] Translated using Weblate (Persian) Currently translated at 0.3% (2 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fa/ --- locales/fa.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/locales/fa.json b/locales/fa.json index 0967ef424..3c8761b5a 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -1 +1,4 @@ -{} +{ + "action_invalid": "اقدام نامعتبر '{action}'", + "aborting": "رها کردن." +} From e7e891574e7600a5ae47cc5d9910e480410abb03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Gaspar?= Date: Tue, 31 Aug 2021 10:11:18 +0000 Subject: [PATCH 100/114] Translated using Weblate (French) Currently translated at 100.0% (643 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 1cac6bbba..0b5a5e93f 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -571,7 +571,7 @@ "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 yunohost dyndns update --force.", "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}", - "global_settings_setting_backup_compress_tar_archives": "Compresser les archives (.tar.gz) au lieu de créer des archives non-compressées lors de la création des sauvegardes. N.B. Activer cette option permet de créer des archives plus légères, mais aussi d'avoir des procédures de sauvegarde significativement plus longues et plus gourmandes en CPU.", + "global_settings_setting_backup_compress_tar_archives": "Lors de la création de nouvelles sauvegardes, compressez les archives (.tar.gz) au lieu des archives non compressées (.tar). N.B. : activer cette option permet de créer des archives plus légères, mais la procédure de sauvegarde initiale sera significativement plus longues et plus gourmandes en CPU.", "migration_description_0018_xtable_to_nftable": "Migrer les anciennes règles de trafic réseau vers le nouveau système basé sur nftables", "service_description_php7.3-fpm": "Exécute les applications écrites en PHP avec NGINX", "migration_0018_failed_to_reset_legacy_rules": "La réinitialisation des règles iptable par défaut a échoué : {error}", @@ -586,7 +586,7 @@ "app_manifest_install_ask_is_public": "Cette application devrait-elle être visible par les visiteurs anonymes ?", "app_manifest_install_ask_admin": "Choisissez un administrateur pour cette application", "app_manifest_install_ask_password": "Choisissez un mot de passe administrateur pour cette application", - "app_manifest_install_ask_path": "Choisissez le chemin sur lequel vous souhaitez installer cette application", + "app_manifest_install_ask_path": "Choisissez le chemin d'URL (après le domaine) où cette application doit être installée", "app_manifest_install_ask_domain": "Choisissez le domaine sur lequel vous souhaitez installer cette application", "global_settings_setting_smtp_relay_user": "Compte utilisateur du relais SMTP", "global_settings_setting_smtp_relay_port": "Port du relais SMTP", @@ -637,5 +637,9 @@ "global_settings_setting_security_webadmin_allowlist": "Adresses IP autorisées à accéder à la page web du portail d'administration (webadmin). Elles doivent être séparées par une virgule.", "global_settings_setting_security_webadmin_allowlist_enabled": "Autorisez seulement certaines IP à accéder à la page web du portail d'administration (webadmin).", "diagnosis_http_localdomain": "Le domaine {domain}, avec un TLD .local, ne devrait pas être atteint depuis l'extérieur du réseau local.", - "diagnosis_dns_specialusedomain": "Le domaine {domain} est basé sur un domaine de premier niveau (TLD) à usage spécial et ne devrait donc pas avoir d'enregistrements DNS réels." -} \ No newline at end of file + "diagnosis_dns_specialusedomain": "Le domaine {domain} est basé sur un domaine de premier niveau (TLD) à usage spécial et ne devrait donc pas avoir d'enregistrements DNS réels.", + "invalid_password": "Mot de passe incorrect", + "ldap_server_is_down_restart_it": "Le service LDAP est en panne, essayez de le redémarrer...", + "ldap_server_down": "Impossible d'atteindre le serveur LDAP", + "global_settings_setting_security_experimental_enabled": "Activez les fonctionnalités de sécurité expérimentales (ne l'activez pas si vous ne savez pas ce que vous faites !)" +} From dd6c58a7dac71c3ecc6831be93d0b216875b1381 Mon Sep 17 00:00:00 2001 From: Parviz Homayun Date: Tue, 31 Aug 2021 11:27:56 +0000 Subject: [PATCH 101/114] Translated using Weblate (Persian) Currently translated at 2.9% (19 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fa/ --- locales/fa.json | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/locales/fa.json b/locales/fa.json index 3c8761b5a..5a2b61bc1 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -1,4 +1,21 @@ { "action_invalid": "اقدام نامعتبر '{action}'", - "aborting": "رها کردن." + "aborting": "رها کردن.", + "app_change_url_failed_nginx_reload": "NGINX بارگیری نشد. در اینجا خروجی 'nginx -t' است:\n{nginx_errors}", + "app_argument_required": "استدلال '{name}' الزامی است", + "app_argument_password_no_default": "خطا هنگام تجزیه گذرواژه '{name}': به دلایل امنیتی استدلال رمز عبور نمی تواند مقدار پیش فرض داشته باشد", + "app_argument_invalid": "یک مقدار معتبر انتخاب کنید برای استدلال '{name}':{error}", + "app_argument_choice_invalid": "برای آرگومان '{name}' از یکی از این گزینه ها '{choices}' استفاده کنید", + "app_already_up_to_date": "{app} در حال حاضر به روز است", + "app_already_installed_cant_change_url": "این برنامه قبلاً نصب شده است. URL فقط با این عملکرد قابل تغییر نیست. در صورت موجود بودن برنامه `app changeurl` را بررسی کنید.", + "app_already_installed": "{app} قبلاً نصب شده است", + "app_action_broke_system": "این اقدام به نظر می رسد سرویس های مهمی را خراب کرده است: {services}", + "app_action_cannot_be_ran_because_required_services_down": "برای اجرای این عملیات سرویس هایی که مورد نیازاند و باید اجرا شوند: {services}. سعی کنید آنها را مجدداً راه اندازی کنید (و علت خرابی احتمالی آنها را بررسی کنید).", + "already_up_to_date": "کاری برای انجام دادن نیست. همه چیز در حال حاضر به روز است.", + "admin_password_too_long": "لطفاً گذرواژه ای کوتاهتر از 127 کاراکتر انتخاب کنید", + "admin_password_changed": "رمز مدیریت تغییر کرد", + "admin_password_change_failed": "تغییر رمز امکان پذیر نیست", + "admin_password": "رمز عبور مدیریت", + "additional_urls_already_removed": "نشانی اینترنتی اضافی '{url}' قبلاً در نشانی اینترنتی اضافی برای اجازه '{permission}'حذف شده است", + "additional_urls_already_added": "نشانی اینترنتی اضافی '{url}' قبلاً در نشانی اینترنتی اضافی برای اجازه '{permission}' اضافه شده است" } From 3264c4fc2d5030d1ea3c64184a35b80d876677be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M?= Date: Tue, 31 Aug 2021 15:58:42 +0000 Subject: [PATCH 102/114] Translated using Weblate (Galician) Currently translated at 90.9% (585 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/gl/ --- locales/gl.json | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/locales/gl.json b/locales/gl.json index 35a1b7390..e752716cd 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -543,5 +543,46 @@ "regenconf_now_managed_by_yunohost": "O ficheiro de configuración '{conf}' agora está xestionado por YunoHost (categoría {category}).", "regenconf_file_updated": "Actualizado o ficheiro de configuración '{conf}'", "regenconf_file_removed": "Eliminado o ficheiro de configuración '{conf}'", - "regenconf_file_remove_failed": "Non se puido eliminar o ficheiro de configuración '{conf}'" + "regenconf_file_remove_failed": "Non se puido eliminar o ficheiro de configuración '{conf}'", + "service_enable_failed": "Non se puido facer que o servizo '{service}' se inicie automáticamente no inicio.\n\nRexistros recentes do servizo: {logs}", + "service_disabled": "O servizo '{service}' xa non vai volver a ser iniciado ao inicio do sistema.", + "service_disable_failed": "Non se puido iniciar o servizo '{servizo}' ao inicio.\n\nRexistro recente do servizo: {logs}", + "service_description_yunohost-firewall": "Xestiona, abre e pecha a conexións dos portos aos servizos", + "service_description_yunohost-api": "Xestiona as interaccións entre a interface web de YunoHost e o sistema", + "service_description_ssh": "Permíteche conectar de xeito remoto co teu servidor a través dun terminal (protocolo SSH)", + "service_description_slapd": "Almacena usuarias, dominios e info relacionada", + "service_description_rspamd": "Filtra spam e outras características relacionadas co email", + "service_description_redis-server": "Unha base de datos especial utilizada para o acceso rápido a datos, cola de tarefas e comunicación entre programas", + "service_description_postfix": "Utilizado para enviar e recibir emails", + "service_description_php7.3-fpm": "Executa aplicacións escritas en PHP con NGINX", + "service_description_nginx": "Serve ou proporciona acceso a tódolos sitios web hospedados no teu servidor", + "service_description_mysql": "Almacena datos da app (base de datos SQL)", + "service_description_metronome": "Xestiona as contas de mensaxería instantánea XMPP", + "service_description_fail2ban": "Protexe contra ataques de forza bruta e outro tipo de ataques desde internet", + "service_description_dovecot": "Permite aos clientes de email acceder/obter o correo (vía IMAP e POP3)", + "service_description_dnsmasq": "Xestiona a resolución de nomes de dominio (DNS)", + "service_description_yunomdns": "Permíteche chegar ao teu servidor utilizando 'yunohost.local' na túa rede local", + "service_cmd_exec_failed": "Non se puido executar o comando '{command}'", + "service_already_stopped": "O servizo '{sevice}' xa está detido", + "service_already_started": "O servizo '{service}' xa se está a executar", + "service_added": "Foi engadido o servizo '{service}'", + "service_add_failed": "Non se puido engadir o servizo '{service}'", + "server_reboot_confirm": "Queres reiniciar o servidor inmediatamente? [{answers}]", + "server_reboot": "Vaise reiniciar o servidor", + "server_shutdown_confirm": "Queres apagar o servidor inmediatamente? [{answers}]", + "server_shutdown": "Vaise apagar o servidor", + "root_password_replaced_by_admin_password": "O contrasinal root foi substituído polo teu contrasinal de administración.", + "root_password_desynchronized": "Mudou o contrasinal de administración, pero YunoHost non puido transferir este cambio ao contrasinal root!", + "restore_system_part_failed": "Non se restableceu a parte do sistema '{part}'", + "restore_running_hooks": "Executando os ganchos do restablecemento…", + "restore_running_app_script": "Restablecendo a app '{app}'…", + "restore_removing_tmp_dir_failed": "Non se puido eliminar o directorio temporal antigo", + "restore_nothings_done": "Nada foi restablecido", + "restore_not_enough_disk_space": "Non hai espazo abondo (espazo: {free_space.d} B, espazo necesario: {needed_space} B, marxe de seguridade: {margin:d} B)", + "restore_may_be_not_enough_disk_space": "O teu sistema semella que non ten espazo abondo (libre: {free_space:d} B, espazo necesario: {needed_space:d} B, marxe de seguridade {margin:d} B)", + "restore_hook_unavailable": "O script de restablecemento para '{part}' non está dispoñible no teu sistema nin no arquivo", + "invalid_password": "Contrasinal non válido", + "ldap_server_is_down_restart_it": "O servidor LDAP está caído, intenta reinicialo...", + "ldap_server_down": "Non se chegou ao servidor LDAP", + "global_settings_setting_security_experimental_enabled": "Activar características de seguridade experimentais (non actives isto se non sabes o que estás a facer!)" } From 2b2335a08c2241a4cec2c531de08538052f9756b Mon Sep 17 00:00:00 2001 From: Parviz Homayun Date: Tue, 31 Aug 2021 18:16:51 +0000 Subject: [PATCH 103/114] Translated using Weblate (Persian) Currently translated at 30.7% (198 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fa/ --- locales/fa.json | 181 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 1 deletion(-) diff --git a/locales/fa.json b/locales/fa.json index 5a2b61bc1..48e18a78e 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -17,5 +17,184 @@ "admin_password_change_failed": "تغییر رمز امکان پذیر نیست", "admin_password": "رمز عبور مدیریت", "additional_urls_already_removed": "نشانی اینترنتی اضافی '{url}' قبلاً در نشانی اینترنتی اضافی برای اجازه '{permission}'حذف شده است", - "additional_urls_already_added": "نشانی اینترنتی اضافی '{url}' قبلاً در نشانی اینترنتی اضافی برای اجازه '{permission}' اضافه شده است" + "additional_urls_already_added": "نشانی اینترنتی اضافی '{url}' قبلاً در نشانی اینترنتی اضافی برای اجازه '{permission}' اضافه شده است", + "diagnosis_diskusage_low": "‏ذخیره سازی {mountpoint} (روی دستگاه {device}) فقط {free} ({free_percent}%) فضا باقی مانده است (از {total}). مراقب باشید.", + "diagnosis_diskusage_verylow": "‏ذخیره سازی {mountpoint} (روی دستگاه {device}) فقط {free} ({free_percent}%) فضا باقی مانده است (از {total}). شما واقعاً باید پاکسازی فضای ذخیره ساز را در نظر بگیرید!", + "diagnosis_services_bad_status_tip": "می توانید سعی کنید سرویس را راه اندازی مجدد کنید، و اگر کار نمی کند ، نگاهی داشته باشید بهسرویس در webadmin ثبت می شود (از خط فرمان ، می توانید این کار را انجام دهید با yunohost service restart {service} و yunohost service log {service}).", + "diagnosis_services_bad_status": "سرویس {service} {status} است :(", + "diagnosis_services_conf_broken": "پیکربندی سرویس {service} خراب است!", + "diagnosis_services_running": "سرویس {service} در حال اجرا است!", + "diagnosis_domain_expires_in": "{domain} در {days} روز منقضی می شود.", + "diagnosis_domain_expiration_error": "برخی از دامنه ها به زودی منقضی می شوند!", + "diagnosis_domain_expiration_warning": "برخی از دامنه ها به زودی منقضی می شوند!", + "diagnosis_domain_expiration_success": "دامنه های شما ثبت شده است و به این زودی منقضی نمی شود.", + "diagnosis_domain_expiration_not_found_details": "به نظر می رسد اطلاعات WHOIS برای دامنه {domain} حاوی اطلاعات مربوط به تاریخ انقضا نیست؟", + "diagnosis_domain_not_found_details": "دامنه {domain} در پایگاه داده WHOIS وجود ندارد یا منقضی شده است!", + "diagnosis_domain_expiration_not_found": "بررسی تاریخ انقضا برخی از دامنه ها امکان پذیر نیست", + "diagnosis_dns_specialusedomain": "دامنه {domain} بر اساس یک دامنه سطح بالا (TLD) مخصوص استفاده است و بنابراین انتظار نمی رود که دارای سوابق DNS واقعی باشد.", + "diagnosis_dns_try_dyndns_update_force": "پیکربندی DNS این دامنه باید به طور خودکار توسط YunoHost مدیریت شود. اگر اینطور نیست ، می توانید سعی کنید به زور یک به روز رسانی را با استفاده از yunohost dyndns update --force.", + "diagnosis_dns_point_to_doc": "لطفاً اسناد را در https://yunohost.org/dns_config برسی و مطالعه کنید، اگر در مورد پیکربندی سوابق DNS به کمک نیاز دارید.", + "diagnosis_dns_discrepancy": "به نظر می رسد پرونده DNS زیر از پیکربندی توصیه شده پیروی نمی کند:
نوع: {type}
نام: {name}
ارزش فعلی: {current}
مقدار مورد انتظار: {value}", + "diagnosis_dns_missing_record": "با توجه به پیکربندی DNS توصیه شده ، باید یک رکورد DNS با اطلاعات زیر اضافه کنید.
نوع: {type}
نام: {name}
ارزش: {value}", + "diagnosis_dns_bad_conf": "برخی از سوابق DNS برای دامنه {domain} (دسته {category}) وجود ندارد یا نادرست است", + "diagnosis_dns_good_conf": "سوابق DNS برای دامنه {domain} (دسته {category}) به درستی پیکربندی شده است", + "diagnosis_ip_weird_resolvconf_details": "پرونده /etc/resolv.conf باید یک پیوند همراه برای /etc/resolvconf/run/resolv.conf خود اشاره می کند به 127.0.0.1 (dnsmasq). اگر می خواهید راه حل های DNS را به صورت دستی پیکربندی کنید ، لطفاً ویرایش کنید /etc/resolv.dnsmasq.conf.", + "diagnosis_ip_weird_resolvconf": "اینطور که پیداست تفکیک پذیری DNS کار می کند ، اما به نظر می رسد از سفارشی استفاده می کنید /etc/resolv.conf.", + "diagnosis_ip_broken_resolvconf": "به نظر می رسد تفکیک پذیری نام دامنه در سرور شما شکسته شده است ، که به نظر می رسد مربوط به /etc/resolv.conf و اشاره نکردن به 127.0.0.1 میباشد.", + "diagnosis_ip_broken_dnsresolution": "به نظر می رسد تفکیک پذیری نام دامنه به دلایلی خراب شده است... آیا فایروال درخواست های DNS را مسدود می کند؟", + "diagnosis_ip_dnsresolution_working": "تفکیک پذیری نام دامنه کار می کند!", + "diagnosis_ip_not_connected_at_all": "به نظر می رسد سرور اصلا به اینترنت متصل نیست !؟", + "diagnosis_ip_local": "IP محلی: {local}", + "diagnosis_ip_global": "IP جهانی: {global}", + "diagnosis_ip_no_ipv6_tip": "داشتن یک IPv6 فعال برای کار سرور شما اجباری نیست ، اما برای سلامت اینترنت به طور کلی بهتر است. IPv6 معمولاً باید در صورت موجود بودن توسط سیستم یا ارائه دهنده اینترنت شما به طور خودکار پیکربندی شود. در غیر این صورت ، ممکن است لازم باشد چند مورد را به صورت دستی پیکربندی کنید ، همانطور که در اسناد اینجا توضیح داده شده است: https://yunohost.org/#/ipv6.اگر نمی توانید IPv6 را فعال کنید یا اگر برای شما بسیار فنی به نظر می رسد ، می توانید با خیال راحت این هشدار را نادیده بگیرید.", + "diagnosis_ip_no_ipv6": "سرور IPv6 کار نمی کند.", + "diagnosis_ip_connected_ipv6": "سرور از طریق IPv6 به اینترنت متصل است!", + "diagnosis_ip_no_ipv4": "سرور IPv4 کار نمی کند.", + "diagnosis_ip_connected_ipv4": "سرور از طریق IPv4 به اینترنت متصل است!", + "diagnosis_no_cache": "هنوز هیچ حافظه نهانی معاینه و عیب یابی برای دسته '{category}' وجود ندارد", + "diagnosis_failed": "نتیجه معاینه و عیب یابی برای دسته '{category}' واکشی نشد: {error}", + "diagnosis_everything_ok": "همه چیز برای {category} خوب به نظر می رسد!", + "diagnosis_found_warnings": "مورد (های) {warnings} یافت شده که می تواند دسته {category} را بهبود بخشد.", + "diagnosis_found_errors_and_warnings": "{errors} مسائل مهم (و {warnings} هشدارها) مربوط به {category} پیدا شد!", + "diagnosis_found_errors": "{errors} مشکلات مهم مربوط به {category} پیدا شد!", + "diagnosis_ignored_issues": "(+ {nb_ignored} مسئله (ها) نادیده گرفته شده)", + "diagnosis_cant_run_because_of_dep": "در حالی که مشکلات مهمی در ارتباط با {dep} وجود دارد ، نمی توان عیب یابی را برای {category} اجرا کرد.", + "diagnosis_cache_still_valid": "(حافظه پنهان هنوز برای عیب یابی {category} معتبر است. هنوز دوباره تشخیص داده نمی شود!)", + "diagnosis_failed_for_category": "عیب یابی برای دسته '{category}' ناموفق بود: {error}", + "diagnosis_display_tip": "برای مشاهده مسائل پیدا شده ، می توانید به بخش تشخیص webadmin بروید یا از خط فرمان 'yunohost diagnosis show --issues --human-readable' را اجرا کنید.", + "diagnosis_package_installed_from_sury_details": "برخی از بسته ها ناخواسته از مخزن شخص ثالث به نام Sury نصب شده اند. تیم YunoHost استراتژی مدیریت این بسته ها را بهبود بخشیده ، اما انتظار می رود برخی از تنظیماتی که برنامه های PHP7.3 را در حالی که هنوز بر روی Stretch نصب شده اند نصب کرده اند ، ناسازگاری های باقی مانده ای داشته باشند. برای رفع این وضعیت ، باید دستور زیر را اجرا کنید: {cmd_to_fix}", + "diagnosis_package_installed_from_sury": "برخی از بسته های سیستمی باید کاهش یابد", + "diagnosis_backports_in_sources_list": "به نظر می رسد apt (مدیریت بسته) برای استفاده از مخزن پشتیبان پیکربندی شده است. مگر اینکه واقعاً بدانید چه کار می کنید ، ما به شدت از نصب بسته های پشتیبان خودداری می کنیم، زیرا به احتمال زیاد باعث ایجاد ناپایداری یا تداخل در سیستم شما می شود.", + "diagnosis_basesystem_ynh_inconsistent_versions": "شما نسخه های ناسازگار از بسته های YunoHost را اجرا می کنید... به احتمال زیاد به دلیل ارتقاء ناموفق یا جزئی است.", + "diagnosis_basesystem_ynh_main_version": "سرور نسخه YunoHost {main_version} ({repo}) را اجرا می کند", + "diagnosis_basesystem_ynh_single_version": "{package} نسخه: {version} ({repo})", + "diagnosis_basesystem_kernel": "سرور نسخه {kernel_version} هسته لینوکس را اجرا می کند", + "diagnosis_basesystem_host": "سرور نسخه {debian_version} دبیان را اجرا می کند", + "diagnosis_basesystem_hardware_model": "مدل سرور {model} میباشد", + "diagnosis_basesystem_hardware": "معماری سخت افزاری سرور {virt} {arch} است", + "custom_app_url_required": "برای ارتقاء سفارشی برنامه {app} خود باید نشانی اینترنتی ارائه دهید", + "confirm_app_install_thirdparty": "خطرناک! این برنامه بخشی از فهرست برنامه YunoHost نیست. نصب برنامه های شخص ثالث ممکن است یکپارچگی و امنیت سیستم شما را به خطر بیندازد. احتمالاً نباید آن را نصب کنید مگر اینکه بدانید در حال انجام چه کاری هستید. اگر این برنامه کار نکرد یا سیستم شما را خراب کرد ، هیچ پشتیبانی ارائه نخواهدشد... به هر حال اگر مایل به پذیرش این خطر هستید ، '{answers}' را تایپ کنید", + "confirm_app_install_danger": "خطرناک! این برنامه هنوز آزمایشی است (اگر صراحتاً کار نکند)! احتمالاً نباید آن را نصب کنید مگر اینکه بدانید در حال انجام چه کاری هستید. اگر این برنامه کار نکرد یا سیستم شما را خراب کرد، هیچ پشتیبانی ارائه نخواهد شد... اگر به هر حال مایل به پذیرش این خطر هستید ، '{answers}' را تایپ کنید", + "confirm_app_install_warning": "هشدار: این برنامه ممکن است کار کند ، اما در YunoHost یکپارچه نشده است. برخی از ویژگی ها مانند ورود به سیستم و پشتیبان گیری/بازیابی ممکن است در دسترس نباشد. به هر حال نصب شود؟ [{answers}] ", + "certmanager_unable_to_parse_self_CA_name": "نتوانست نام مرجع خودامضائی را تجزیه و تحلیل کند (فایل: {file})", + "certmanager_self_ca_conf_file_not_found": "فایل پیکربندی برای اجازه خود امضائی پیدا نشد (فایل: {file})", + "certmanager_no_cert_file": "فایل گواهینامه برای دامنه {domain} خوانده نشد (فایل: {file})", + "certmanager_hit_rate_limit": "اخیراً تعداد زیادی گواهی برای این مجموعه دقیق از دامنه ها {domain} صادر شده است. لطفاً بعداً دوباره امتحان کنید. برای جزئیات بیشتر به https://letsencrypt.org/docs/rate-limits/ مراجعه کنید", + "certmanager_warning_subdomain_dns_record": "آدرس زیر دامنه '{subdomain}' به آدرس IP مشابه '{domain}' تبدیل نمی شود. تا زمانی که این مشکل را برطرف نکنید و گواهی را دوباره ایجاد نکنید ، برخی از ویژگی ها در دسترس نخواهند بود.", + "certmanager_domain_http_not_working": "به نظر می رسد دامنه {domain} از طریق HTTP قابل دسترسی نیست. لطفاً برای اطلاعات بیشتر ، دسته \"وب\" را در عیب یابی بررسی کنید. (اگر می دانید چه کار می کنید ، از '--no-checks' برای خاموش کردن این چک ها استفاده کنید.)", + "certmanager_domain_dns_ip_differs_from_public_ip": "سوابق DNS برای دامنه '{domain}' با IP این سرور متفاوت است. لطفاً برای اطلاعات بیشتر ، دسته 'DNS records' (پایه) را در عیب یابی بررسی کنید. اگر اخیراً رکورد A خود را تغییر داده اید ، لطفاً منتظر انتشار آن باشید (برخی از چکرهای انتشار DNS بصورت آنلاین در دسترس هستند). (اگر می دانید چه کار می کنید ، از '--no-checks' برای خاموش کردن این چک ها استفاده کنید.)", + "certmanager_domain_cert_not_selfsigned": "گواهی دامنه {domain} خود امضا نشده است. آیا مطمئن هستید که می خواهید آن را جایگزین کنید؟ (برای این کار از '--force' استفاده کنید.)", + "certmanager_domain_not_diagnosed_yet": "هنوز هیچ نتیجه تشخیصی و عیب یابی دامنه {domain} وجود ندارد. لطفاً در بخش عیب یابی ، دسته های 'DNS records' و 'Web'مجدداً عیب یابی را اجرا کنید تا بررسی شود که آیا دامنه ای برای گواهی اجازه رمزنگاری آماده است. (یا اگر می دانید چه کار می کنید ، از '--no-checks' برای خاموش کردن این بررسی ها استفاده کنید.)", + "certmanager_certificate_fetching_or_enabling_failed": "تلاش برای استفاده از گواهینامه جدید برای {domain} جواب نداد...", + "certmanager_cert_signing_failed": "گواهی جدید امضا نشده است", + "certmanager_cert_renew_success": "گواهی اجازه رمزنگاری برای دامنه '{domain}' تمدید شد", + "certmanager_cert_install_success_selfsigned": "گواهی خود امضا شده اکنون برای دامنه '{domain}' نصب شده است", + "certmanager_cert_install_success": "هم اینک گواهی اجازه رمزگذاری برای دامنه '{domain}' نصب شده است", + "certmanager_cannot_read_cert": "هنگام باز کردن گواهینامه فعلی مشکلی پیش آمده است برای دامنه {domain} (فایل: {file}) ، علّت: {reason}", + "certmanager_attempt_to_replace_valid_cert": "شما در حال تلاش برای بازنویسی یک گواهی خوب و معتبر برای دامنه {domain} هستید! (استفاده از --force برای bypass)", + "certmanager_attempt_to_renew_valid_cert": "گواهی دامنه '{domain}' در حال انقضا نیست! (اگر می دانید چه کار می کنید می توانید از --force استفاده کنید)", + "certmanager_attempt_to_renew_nonLE_cert": "گواهی دامنه '{domain}' توسط Let's Encrypt صادر نشده است. به طور خودکار تمدید نمی شود!", + "certmanager_acme_not_configured_for_domain": "در حال حاضر نمی توان چالش ACME را برای {domain} اجرا کرد زیرا nginx conf آن فاقد قطعه کد مربوطه است... لطفاً مطمئن شوید که پیکربندی nginx شما به روز است با استفاده از دستور `yunohost tools regen-conf nginx --dry-run --with-diff`.", + "backup_with_no_restore_script_for_app": "{app} فاقد اسکریپت بازگردانی است ، نمی توانید پشتیبان گیری این برنامه را به طور خودکار بازیابی کنید.", + "backup_with_no_backup_script_for_app": "برنامه '{app}' فاقد اسکریپت پشتیبان است. نادیده گرفتن.", + "backup_unable_to_organize_files": "نمی توان از روش سریع برای سازماندهی فایل ها در بایگانی استفاده کرد", + "backup_system_part_failed": "از بخش سیستم '{part}' پشتیبان گیری نشد", + "backup_running_hooks": "درحال اجرای قلاب پشتیبان گیری...", + "backup_permission": "مجوز پشتیبان گیری برای {app}", + "backup_output_symlink_dir_broken": "فهرست بایگانی شما '{path}' یک پیوند symlink خراب است. شاید فراموش کرده اید که مجدداً محل ذخیره سازی که به آن اشاره می کند را دوباره نصب یا وصل کنید.", + "backup_output_directory_required": "شما باید یک پوشه خروجی برای نسخه پشتیبان تهیه کنید", + "backup_output_directory_not_empty": "شما باید یک دایرکتوری خروجی خالی انتخاب کنید", + "backup_output_directory_forbidden": "دایرکتوری خروجی دیگری را انتخاب کنید. پشتیبان گیری نمی تواند در /bin، /boot، /dev ، /etc ، /lib ، /root ، /run ، /sbin ، /sys ، /usr ، /var یا /home/yunohost.backup/archives ایجاد شود", + "backup_nothings_done": "چیزی برای ذخیره کردن وجود ندارد", + "backup_no_uncompress_archive_dir": "چنین فهرست بایگانی فشرده نشده ایی وجود ندارد", + "backup_mount_archive_for_restore": "در حال آماده سازی بایگانی برای بازگردانی...", + "backup_method_tar_finished": "بایگانی پشتیبان TAR ایجاد شد", + "backup_method_custom_finished": "روش پشتیبان گیری سفارشی '{method}' به پایان رسید", + "backup_method_copy_finished": "نسخه پشتیبان نهایی شد", + "backup_hook_unknown": "قلاب پشتیبان '{hook}' ناشناخته است", + "backup_deleted": "نسخه پشتیبان حذف شد", + "backup_delete_error": "'{path}' حذف نشد", + "backup_custom_mount_error": "روش پشتیبان گیری سفارشی نمی تواند از مرحله 'mount' عبور کند", + "backup_custom_backup_error": "روش پشتیبان گیری سفارشی نمی تواند مرحله 'backup' را پشت سر بگذارد", + "backup_csv_creation_failed": "فایل CSV مورد نیاز برای بازیابی ایجاد نشد", + "backup_csv_addition_failed": "فایلهای پشتیبان به فایل CSV اضافه نشد", + "backup_creation_failed": "نسخه پشتیبان بایگانی ایجاد نشد", + "backup_create_size_estimation": "بایگانی حاوی حدود {size} داده است.", + "backup_created": "نسخه پشتیبان ایجاد شد", + "backup_couldnt_bind": "نمی توان {src} را به {dest} متصل کرد.", + "backup_copying_to_organize_the_archive": "در حال کپی {size} مگابایت برای سازماندهی بایگانی", + "backup_cleaning_failed": "پوشه موقت پشتیبان گیری پاکسازی نشد", + "backup_cant_mount_uncompress_archive": "بایگانی فشرده سازی نشده را نمی توان به عنوان حفاظت از نوشتن مستقر کرد", + "backup_ask_for_copying_if_needed": "آیا می خواهید پشتیبان گیری را با استفاده از {size} مگابایت به طور موقت انجام دهید؟ (این روش استفاده می شود زیرا برخی از پرونده ها با استفاده از روش کارآمدتری تهیه نمی شوند.)", + "backup_archive_writing_error": "فایل های '{source}' (که در بایگانی '{dest}' نامگذاری شده اند) برای پشتیبان گیری به بایگانی فشرده '{archive}' اضافه نشد", + "backup_archive_system_part_not_available": "بخش سیستم '{part}' در این نسخه پشتیبان در دسترس نیست", + "backup_archive_corrupted": "به نظر می رسد بایگانی پشتیبان '{archive}' خراب است: {error}", + "backup_archive_cant_retrieve_info_json": "اطلاعات مربوط به بایگانی '{archive}' بارگیری نشد... info.json بازیابی نمی شود (یا json معتبری نیست).", + "backup_archive_open_failed": "بایگانی پشتیبان باز نشد", + "backup_archive_name_unknown": "بایگانی پشتیبان محلی ناشناخته با نام '{name}'", + "backup_archive_name_exists": "بایگانی پشتیبان با این نام در حال حاضر وجود دارد.", + "backup_archive_broken_link": "دسترسی به بایگانی پشتیبان امکان پذیر نیست (پیوند خراب به {path})", + "backup_archive_app_not_found": "در بایگانی پشتیبان {app} پیدا نشد", + "backup_applying_method_tar": "ایجاد آرشیو پشتیبان TAR...", + "backup_applying_method_custom": "فراخوانی روش پشتیبان گیری سفارشی '{method}'...", + "backup_applying_method_copy": "در حال کپی تمام فایل ها برای پشتیبان گیری...", + "backup_app_failed": "{app} پشتیبان گیری نشد", + "backup_actually_backuping": "ایجاد آرشیو پشتیبان از پرونده های جمع آوری شده...", + "backup_abstract_method": "این روش پشتیبان گیری هنوز اجرا نشده است", + "ask_password": "رمز عبور", + "ask_new_path": "مسیر جدید", + "ask_new_domain": "دامنه جدید", + "ask_new_admin_password": "رمز جدید مدیریت", + "ask_main_domain": "دامنه اصلی", + "ask_lastname": "نام خانوادگی", + "ask_firstname": "نام کوچک", + "ask_user_domain": "دامنه ای که برای آدرس ایمیل کاربر و حساب XMPP استفاده می شود", + "apps_catalog_update_success": "کاتالوگ برنامه به روز شد!", + "apps_catalog_obsolete_cache": "حافظه پنهان کاتالوگ برنامه خالی یا منسوخ شده است.", + "apps_catalog_failed_to_download": "بارگیری کاتالوگ برنامه {apps_catalog} امکان پذیر نیست: {error}", + "apps_catalog_updating": "در حال به روز رسانی کاتالوگ برنامه…", + "apps_catalog_init_success": "سیستم کاتالوگ برنامه راه اندازی اولیه شد!", + "apps_already_up_to_date": "همه برنامه ها در حال حاضر به روز هستند", + "app_packaging_format_not_supported": "این برنامه قابل نصب نیست زیرا قالب بسته بندی آن توسط نسخه YunoHost شما پشتیبانی نمی شود. احتمالاً باید ارتقاء سیستم خود را در نظر بگیرید.", + "app_upgraded": "{app} ارتقا یافت", + "app_upgrade_some_app_failed": "برخی از برنامه ها را نمی توان ارتقا داد", + "app_upgrade_script_failed": "خطایی در داخل اسکریپت ارتقاء برنامه رخ داده است", + "app_upgrade_failed": "{app} ارتقاء نیافت: {error}", + "app_upgrade_app_name": "در حال ارتقاء {app}...", + "app_upgrade_several_apps": "برنامه های زیر ارتقا می یابند: {apps}", + "app_unsupported_remote_type": "نوع راه دور پشتیبانی نشده برای برنامه استفاده می شود", + "app_unknown": "برنامه ناشناخته", + "app_start_restore": "درحال بازیابی {app}...", + "app_start_backup": "در حال جمع آوری فایل ها برای پشتیبان گیری {app}...", + "app_start_remove": "در حال حذف {app}...", + "app_start_install": "در حال نصب {app}...", + "app_sources_fetch_failed": "نمی توان فایل های منبع را واکشی کرد ، آیا URL درست است؟", + "app_restore_script_failed": "خطایی در داخل اسکریپت بازیابی برنامه رخ داده است", + "app_restore_failed": "{app} بازیابی نشد: {error}", + "app_remove_after_failed_install": "حذف برنامه در پی شکست نصب...", + "app_requirements_unmeet": "شرایط مورد نیاز برای {app} برآورده نمی شود ، بسته {pkgname} ({version}) باید {spec} باشد", + "app_requirements_checking": "در حال بررسی بسته های مورد نیاز برای {app}...", + "app_removed": "{app} حذف نصب شد", + "app_not_properly_removed": "{app} به درستی حذف نشده است", + "app_not_installed": "{app} در لیست برنامه های نصب شده یافت نشد: {all_apps}", + "app_not_correctly_installed": "به نظر می رسد {app} به اشتباه نصب شده است", + "app_not_upgraded": "برنامه '{failed_app}' ارتقا پیدا نکرد و در نتیجه ارتقا برنامه های زیر لغو شد: {apps}", + "app_manifest_install_ask_is_public": "آیا این برنامه باید در معرض دید بازدیدکنندگان ناشناس قرار گیرد؟", + "app_manifest_install_ask_admin": "برای این برنامه یک کاربر سرپرست انتخاب کنید", + "app_manifest_install_ask_password": "گذرواژه مدیریتی را برای این برنامه انتخاب کنید", + "app_manifest_install_ask_path": "مسیر URL (بعد از دامنه) را انتخاب کنید که این برنامه باید در آن نصب شود", + "app_manifest_install_ask_domain": "دامنه ای را انتخاب کنید که این برنامه باید در آن نصب شود", + "app_manifest_invalid": "مشکلی در مانیفست برنامه وجود دارد: {error}", + "app_location_unavailable": "این نشانی وب یا در دسترس نیست یا با برنامه (هایی) که قبلاً نصب شده در تعارض است:\n{apps}", + "app_label_deprecated": "این دستور منسوخ شده است! لطفاً برای مدیریت برچسب برنامه از فرمان جدید'yunohost به روز رسانی مجوز کاربر' استفاده کنید.", + "app_make_default_location_already_used": "نمی توان '{app}' را برنامه پیش فرض در دامنه قرار داد ، '{domain}' قبلاً توسط '{other_app}' استفاده می شود", + "app_install_script_failed": "خطایی در درون اسکریپت نصب برنامه رخ داده است", + "app_install_failed": "نصب {app} امکان پذیر نیست: {error}", + "app_install_files_invalid": "این فایل ها قابل نصب نیستند", + "app_id_invalid": "شناسه برنامه نامعتبر است", + "app_full_domain_unavailable": "متأسفیم ، این برنامه باید در دامنه خود نصب شود ، اما سایر برنامه ها قبلاً در دامنه '{domain}' نصب شده اند.شما به جای آن می توانید از یک زیر دامنه اختصاص داده شده به این برنامه استفاده کنید.", + "app_extraction_failed": "فایل های نصبی استخراج نشد", + "app_change_url_success": "{app} URL اکنون {domain} {path} است", + "app_change_url_no_script": "برنامه '{app_name}' هنوز از تغییر URL پشتیبانی نمی کند. شاید باید آن را ارتقا دهید.", + "app_change_url_identical_domains": "دامنه /url_path قدیمی و جدیدیکسان هستند ('{domain}{path}') ، کاری برای انجام دادن نیست." } From 199e65e41ccb34b3f097b58a7dd2124174dc8110 Mon Sep 17 00:00:00 2001 From: mifegui Date: Tue, 31 Aug 2021 22:11:22 +0000 Subject: [PATCH 104/114] Translated using Weblate (Portuguese) Currently translated at 14.4% (93 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/pt/ --- locales/pt.json | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/locales/pt.json b/locales/pt.json index cc47da946..4b4248f09 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -8,12 +8,12 @@ "app_id_invalid": "App ID invaĺido", "app_install_files_invalid": "Esses arquivos não podem ser instalados", "app_manifest_invalid": "Manifesto da aplicação inválido: {error}", - "app_not_installed": "{app} não está instalada", - "app_removed": "{app} removida com êxito", - "app_sources_fetch_failed": "Incapaz obter os ficheiros fonte", + "app_not_installed": "Não foi possível encontrar {app} na lista de aplicações instaladas: {all_apps}", + "app_removed": "{app} desinstalada", + "app_sources_fetch_failed": "Não foi possível carregar os arquivos de código fonte, a URL está correta?", "app_unknown": "Aplicação desconhecida", - "app_upgrade_failed": "Não foi possível atualizar {app}", - "app_upgraded": "{app} atualizada com sucesso", + "app_upgrade_failed": "Não foi possível atualizar {app}: {error}", + "app_upgraded": "{app} atualizado", "ask_firstname": "Primeiro nome", "ask_lastname": "Último nome", "ask_main_domain": "Domínio principal", @@ -114,8 +114,8 @@ "app_argument_invalid": "Escolha um valor válido para o argumento '{name}': {error}", "app_argument_required": "O argumento '{name}' é obrigatório", "app_change_url_failed_nginx_reload": "Não foi possível reiniciar o nginx. Aqui está o retorno de 'nginx -t':\n{nginx_errors}", - "app_location_unavailable": "Esta url não está disponível ou está em conflito com outra aplicação já instalada", - "app_upgrade_app_name": "Atualizando aplicação {app}…", + "app_location_unavailable": "Esta url ou não está disponível ou está em conflito com outra(s) aplicação(ões) já instalada(s):\n{apps}", + "app_upgrade_app_name": "Atualizando {app}…", "app_upgrade_some_app_failed": "Não foi possível atualizar algumas aplicações", "backup_abstract_method": "Este metodo de backup ainda não foi implementado", "backup_app_failed": "Não foi possível fazer o backup dos aplicativos '{app}'", @@ -140,5 +140,29 @@ "app_install_script_failed": "Ocorreu um erro dentro do script de instalação do aplicativo", "app_install_failed": "Não foi possível instalar {app}: {error}", "app_full_domain_unavailable": "Desculpe, esse app deve ser instalado num domínio próprio mas já há outros apps instalados no domínio '{domain}'. Você pode usar um subdomínio dedicado a esse aplicativo.", - "app_change_url_success": "A URL agora é {domain}{path}" -} \ No newline at end of file + "app_change_url_success": "A URL agora é {domain}{path}", + "apps_catalog_obsolete_cache": "O cache do catálogo de aplicações está vazio ou obsoleto.", + "apps_catalog_failed_to_download": "Não foi possível fazer o download do catálogo de aplicações {apps_catalog}: {error}", + "apps_catalog_updating": "Atualizado o catálogo de aplicações…", + "apps_catalog_init_success": "Catálogo de aplicações do sistema inicializado!", + "apps_already_up_to_date": "Todas as aplicações já estão atualizadas", + "app_packaging_format_not_supported": "Essa aplicação não pode ser instalada porque o formato dela não é suportado pela sua versão do YunoHost. Considere atualizar seu sistema.", + "app_upgrade_script_failed": "Ocorreu um erro dentro do script de atualização da aplicação", + "app_upgrade_several_apps": "As seguintes aplicações serão atualizadas: {apps}", + "app_start_restore": "Restaurando {app}...", + "app_start_backup": "Obtendo os arquivos para fazer o backup de {app}...", + "app_start_remove": "Removendo {app}...", + "app_start_install": "Instalando {app}...", + "app_restore_script_failed": "Ocorreu um erro dentro do script de restauração da aplicação", + "app_restore_failed": "Não foi possível restaurar {app}: {error}", + "app_remove_after_failed_install": "Removendo a aplicação após a falha da instalação...", + "app_requirements_unmeet": "Os requisitos para a aplicação {app} não foram satisfeitos, o pacote {pkgname} ({version}) devem ser {spec}", + "app_not_upgraded": "Não foi possível atualizar a aplicação '{failed_app}' e, como consequência, a atualização das seguintes aplicações foi cancelada: {apps}", + "app_manifest_install_ask_is_public": "Essa aplicação deve ser visível para visitantes anônimos?", + "app_manifest_install_ask_admin": "Escolha um usuário de administrador para essa aplicação", + "app_manifest_install_ask_password": "Escolha uma senha de administrador para essa aplicação", + "app_manifest_install_ask_path": "Escolha o caminho da url (depois do domínio) em que essa aplicação deve ser instalada", + "app_manifest_install_ask_domain": "Escolha o domínio em que esta aplicação deve ser instalada", + "app_label_deprecated": "Este comando está deprecado! Por favor use o novo comando 'yunohost user permission update' para gerenciar a etiqueta da aplicação.", + "app_make_default_location_already_used": "Não foi passível fazer a aplicação '{app}' ser a padrão no domínio, '{domain}' já está sendo usado por '{other_app}'" +} From 516a77a4914c63537a52d0041323c09ac758b9b5 Mon Sep 17 00:00:00 2001 From: Parviz Homayun Date: Tue, 31 Aug 2021 21:22:47 +0000 Subject: [PATCH 105/114] Translated using Weblate (Persian) Currently translated at 41.2% (265 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fa/ --- locales/fa.json | 73 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/locales/fa.json b/locales/fa.json index 48e18a78e..c744a8637 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -18,8 +18,8 @@ "admin_password": "رمز عبور مدیریت", "additional_urls_already_removed": "نشانی اینترنتی اضافی '{url}' قبلاً در نشانی اینترنتی اضافی برای اجازه '{permission}'حذف شده است", "additional_urls_already_added": "نشانی اینترنتی اضافی '{url}' قبلاً در نشانی اینترنتی اضافی برای اجازه '{permission}' اضافه شده است", - "diagnosis_diskusage_low": "‏ذخیره سازی {mountpoint} (روی دستگاه {device}) فقط {free} ({free_percent}%) فضا باقی مانده است (از {total}). مراقب باشید.", - "diagnosis_diskusage_verylow": "‏ذخیره سازی {mountpoint} (روی دستگاه {device}) فقط {free} ({free_percent}%) فضا باقی مانده است (از {total}). شما واقعاً باید پاکسازی فضای ذخیره ساز را در نظر بگیرید!", + "diagnosis_diskusage_low": "‏ذخیره سازی {mountpoint} (روی دستگاه {device}) فقط {free} ({free_percent}%) فضا باقی مانده(از {total}). مراقب باشید.", + "diagnosis_diskusage_verylow": "‏ذخیره سازی {mountpoint} (روی دستگاه {device}) فقط {free} ({free_percent}%) فضا باقی مانده (از {total}). شما واقعاً باید پاکسازی فضای ذخیره ساز را در نظر بگیرید!", "diagnosis_services_bad_status_tip": "می توانید سعی کنید سرویس را راه اندازی مجدد کنید، و اگر کار نمی کند ، نگاهی داشته باشید بهسرویس در webadmin ثبت می شود (از خط فرمان ، می توانید این کار را انجام دهید با yunohost service restart {service} و yunohost service log {service}).", "diagnosis_services_bad_status": "سرویس {service} {status} است :(", "diagnosis_services_conf_broken": "پیکربندی سرویس {service} خراب است!", @@ -196,5 +196,72 @@ "app_extraction_failed": "فایل های نصبی استخراج نشد", "app_change_url_success": "{app} URL اکنون {domain} {path} است", "app_change_url_no_script": "برنامه '{app_name}' هنوز از تغییر URL پشتیبانی نمی کند. شاید باید آن را ارتقا دهید.", - "app_change_url_identical_domains": "دامنه /url_path قدیمی و جدیدیکسان هستند ('{domain}{path}') ، کاری برای انجام دادن نیست." + "app_change_url_identical_domains": "دامنه /url_path قدیمی و جدیدیکسان هستند ('{domain}{path}') ، کاری برای انجام دادن نیست.", + "diagnosis_http_connection_error": "خطای اتصال: ارتباط با دامنه درخواست شده امکان پذیر نیست، به احتمال زیاد غیرقابل دسترسی است.", + "diagnosis_http_timeout": "زمان تلاش برای تماس با سرور از خارج به پایان رسید. به نظر می رسد غیرقابل دسترسی است.
1. شایع ترین علت برای این مشکل ، پورت 80 است (و 443) به درستی به سرور شما ارسال نمی شوند.
2. همچنین باید مطمئن شوید که سرویس nginx در حال اجرا است
3. در تنظیمات پیچیده تر: مطمئن شوید که هیچ فایروال یا پروکسی معکوسی تداخل نداشته باشد.", + "diagnosis_http_ok": "دامنه {domain} از طریق HTTP از خارج از شبکه محلی قابل دسترسی است.", + "diagnosis_http_localdomain": "انتظار نمی رود که دامنه {domain} ، با TLD محلی. از خارج از شبکه محلی به آن دسترسی پیدا کند.", + "diagnosis_http_could_not_diagnose_details": "خطا: {error}", + "diagnosis_http_could_not_diagnose": "نمی توان تشخیص داد که در IPv{ipversion} دامنه ها از خارج قابل دسترسی هستند یا خیر.", + "diagnosis_http_hairpinning_issue_details": "این احتمالاً به دلیل جعبه / روتر ISP شما است. در نتیجه ، افراد خارج از شبکه محلی شما می توانند به سرور شما مطابق انتظار دسترسی پیدا کنند ، اما افراد داخل شبکه محلی (احتمالاً مثل شما؟) هنگام استفاده از نام دامنه یا IP جهانی. ممکن است بتوانید وضعیت را بهبود بخشید با نگاهی به https://yunohost.org/dns_local_network", + "diagnosis_http_hairpinning_issue": "به نظر می رسد در شبکه محلی شما hairpinning فعال نشده است.", + "diagnosis_ports_forwarding_tip": "برای رفع این مشکل، به احتمال زیاد باید انتقال پورت را در روتر اینترنت خود پیکربندی کنید همانطور که شرح داده شده در https://yunohost.org/isp_box_config", + "diagnosis_ports_needed_by": "افشای این پورت برای ویژگی های {category} (سرویس {service}) مورد نیاز است", + "diagnosis_ports_ok": "پورت {port} از خارج قابل دسترسی است.", + "diagnosis_ports_partially_unreachable": "پورت {port} از خارج در {failed}IPv قابل دسترسی نیست.", + "diagnosis_ports_unreachable": "پورت {port} از خارج قابل دسترسی نیست.", + "diagnosis_ports_could_not_diagnose_details": "خطا: {error}", + "diagnosis_ports_could_not_diagnose": "نمی توان تشخیص داد پورت ها از خارج در IPv{ipversion} قابل دسترسی هستند یا خیر.", + "diagnosis_description_regenconf": "تنظیمات سیستم", + "diagnosis_description_mail": "ایمیل", + "diagnosis_description_web": "وب", + "diagnosis_description_ports": "ارائه پورت ها", + "diagnosis_description_systemresources": "منابع سیستم", + "diagnosis_description_services": "بررسی وضعیّت سرویس ها", + "diagnosis_description_dnsrecords": "رکورد DNS", + "diagnosis_description_ip": "اتصال به اینترنت", + "diagnosis_description_basesystem": "سیستم پایه", + "diagnosis_security_vulnerable_to_meltdown_details": "برای رفع این مشکل ، باید سیستم خود را ارتقا دهید و مجدداً راه اندازی کنید تا هسته لینوکس جدید بارگیری شود (یا در صورت عدم کارکرد با ارائه دهنده سرور خود تماس بگیرید). برای اطلاعات بیشتر به https://meltdownattack.com/ مراجعه کنید.", + "diagnosis_security_vulnerable_to_meltdown": "به نظر می رسد شما در برابر آسیب پذیری امنیتی بحرانی Meltdown آسیب پذیر هستید", + "diagnosis_rootfstotalspace_critical": "کل سیستم فایل فقط دارای {space} است که بسیار نگران کننده است! احتمالاً خیلی زود فضای دیسک شما تمام می شود! توصیه می شود حداقل 16 گیگابایت و بیشتر فضا برای سیستم فایل ریشه داشته باشید.", + "diagnosis_rootfstotalspace_warning": "سیستم فایل ریشه در مجموع فقط {space} دارد. ممکن است اشکالی نداشته باشد ، اما مراقب باشید زیرا در نهایت ممکن است فضای دیسک شما به سرعت تمام شود... توصیه می شود حداقل 16 گیگابایت و بیشتر فضا برای سیستم فایل ریشه داشته باشید.", + "diagnosis_regenconf_manually_modified_details": "اگر بدانید چه کار می کنید ، احتمالاً خوب است! YunoHost به روز رسانی خودکار این فایل را متوقف می کند... اما مراقب باشید که ارتقاء YunoHost می تواند شامل تغییرات مهم توصیه شده باشد. اگر می خواهید ، می توانید تفاوت ها را با yunohost tools regen-conf {category} --dry-run --with-diff و تنظیم مجدد پیکربندی توصیه شده به زور با فرمان yunohost tools regen-conf {category} --force", + "diagnosis_regenconf_manually_modified": "به نظر می رسد فایل پیکربندی {file} به صورت دستی اصلاح شده است.", + "diagnosis_regenconf_allgood": "همه فایلهای پیکربندی مطابق با تنظیمات توصیه شده است!", + "diagnosis_mail_queue_too_big": "تعداد زیادی ایمیل معلق در صف پست ({nb_pending} ایمیل)", + "diagnosis_mail_queue_unavailable_details": "خطا: {error}", + "diagnosis_mail_queue_unavailable": "نمی توان با تعدادی از ایمیل های معلق در صف مشورت کرد", + "diagnosis_mail_queue_ok": "{nb_pending} ایمیل های معلق در صف های ایمیل", + "diagnosis_mail_blacklist_website": "پس از شناسایی دلیل لیست شدن و رفع آن، با خیال راحت درخواست کنید IP یا دامنه شما حذف شود از {blacklist_website}", + "diagnosis_mail_blacklist_reason": "دلیل لیست سیاه: {reason}", + "diagnosis_mail_blacklist_listed_by": "IP یا دامنه شما {item}در لیست سیاه {blacklist_name} قرار دارد", + "diagnosis_mail_blacklist_ok": "به نظر می رسد IP ها و دامنه های مورد استفاده این سرور در لیست سیاه قرار ندارند", + "diagnosis_mail_fcrdns_different_from_ehlo_domain_details": "DNS معکوس فعلی: {rdns_domain}
مقدار مورد انتظار: {ehlo_domain}", + "diagnosis_mail_fcrdns_different_from_ehlo_domain": "DNS معکوس به درستی در IPv{ipversion} پیکربندی نشده است. ممکن است برخی از ایمیل ها تحویل داده نشوند یا به عنوان هرزنامه پرچم گذاری شوند.", + "diagnosis_mail_fcrdns_nok_alternatives_6": "برخی از ارائه دهندگان به شما اجازه نمی دهند DNS معکوس خود را پیکربندی کنید (یا ممکن است ویژگی آنها شکسته شود...). اگر DNS معکوس شما به درستی برای IPv4 پیکربندی شده است، با استفاده از آن می توانید هنگام ارسال ایمیل، استفاده از IPv6 را غیرفعال کنید. yunohost settings set smtp.allow_ipv6 -v off. توجه: این راه حل آخری به این معنی است که شما نمی توانید از چند سرور IPv6 موجود ایمیل ارسال یا دریافت کنید.", + "diagnosis_mail_fcrdns_nok_alternatives_4": "برخی از ارائه دهندگان به شما اجازه نمی دهند DNS معکوس خود را پیکربندی کنید (یا ممکن است ویژگی آنها شکسته شود...). اگر به همین دلیل مشکلاتی را تجربه می کنید ، راه حل های زیر را در نظر بگیرید: - برخی از ISP ها جایگزین ارائه می دهند با استفاده از رله سرور ایمیل اگرچه به این معنی است که رله می تواند از ترافیک ایمیل شما جاسوسی کند.
- یک جایگزین دوستدار حریم خصوصی استفاده از VPN * با IP عمومی اختصاصی * برای دور زدن این نوع محدودیت ها است. ببینید https://yunohost.org/#/vpn_advantage
- یا ممکن است به ارائه دهنده دیگری بروید", + "diagnosis_mail_fcrdns_nok_details": "ابتدا باید DNS معکوس را پیکربندی کنید با {ehlo_domain} در رابط روتر اینترنت یا رابط ارائه دهنده میزبانی تان. (ممکن است برخی از ارائه دهندگان میزبانی از شما بخواهند که برای این کار تیکت پشتیبانی ارسال کنید).", + "diagnosis_mail_fcrdns_dns_missing": "در IPv{ipversion} هیچ DNS معکوسی تعریف نشده است. ممکن است برخی از ایمیل ها تحویل داده نشوند یا به عنوان هرزنامه پرچم گذاری شوند.", + "diagnosis_mail_fcrdns_ok": "DNS معکوس شما به درستی پیکربندی شده است!", + "diagnosis_mail_ehlo_could_not_diagnose_details": "خطا: {error}", + "diagnosis_mail_ehlo_could_not_diagnose": "نمی توان تشخیص داد که آیا سرور ایمیل postfix از خارج در IPv{ipversion} قابل دسترسی است یا خیر.", + "diagnosis_mail_ehlo_wrong_details": "EHLO دریافت شده توسط تشخیص دهنده از راه دور در IPv{ipversion} با دامنه سرور شما متفاوت است.
EHLO دریافت شده: {wrong_ehlo}
انتظار می رود: {right_ehlo}
شایع ترین علت این مشکل ، پورت 25 است به درستی به سرور شما ارسال نشده است. از سوی دیگر اطمینان حاصل کنید که هیچ فایروال یا پروکسی معکوسی تداخل ایجاد نمی کند.", + "diagnosis_mail_ehlo_wrong": "یک سرور ایمیل SMTP متفاوت در IPv{ipversion} پاسخ می دهد. سرور شما احتمالاً نمی تواند ایمیل دریافت کند.", + "diagnosis_mail_ehlo_bad_answer_details": "ممکن است به دلیل پاسخ دادن دستگاه دیگری به جای سرور شما باشد.", + "diagnosis_mail_ehlo_bad_answer": "یک سرویس غیر SMTP در پورت 25 در IPv{ipversion} پاسخ داد", + "diagnosis_mail_ehlo_unreachable_details": "اتصال روی پورت 25 سرور شما در IPv{ipversion} باز نشد. به نظر می رسد غیرقابل دسترس است.
1. شایع ترین علت این مشکل ، پورت 25 است به درستی به سرور شما ارسال نشده است.
2. همچنین باید مطمئن شوید که سرویس postfix در حال اجرا است.
3. در تنظیمات پیچیده تر: مطمئن شوید که هیچ فایروال یا پروکسی معکوسی تداخل نداشته باشد.", + "diagnosis_mail_ehlo_unreachable": "سرور ایمیل SMTP از خارج در IPv {ipversion} غیرقابل دسترسی است. قادر به دریافت ایمیل نخواهد بود.", + "diagnosis_mail_ehlo_ok": "سرور ایمیل SMTP از خارج قابل دسترسی است و بنابراین می تواند ایمیل دریافت کند!", + "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "برخی از ارائه دهندگان به شما اجازه نمی دهند پورت خروجی 25 را رفع انسداد کنید زیرا به بی طرفی شبکه اهمیتی نمی دهند.
- برخی از آنها جایگزین را ارائه می دهند با استفاده از رله سرور ایمیل اگرچه به این معنی است که رله می تواند از ترافیک ایمیل شما جاسوسی کند.
- یک جایگزین دوستدار حریم خصوصی استفاده از VPN * با IP عمومی اختصاصی * برای دور زدن این نوع محدودیت ها است. ببینید https://yunohost.org/#/vpn_advantage
- همچنین می توانید تغییر را در نظر بگیرید به یک ارائه دهنده بی طرف خالص تر", + "diagnosis_mail_outgoing_port_25_blocked_details": "ابتدا باید سعی کنید پورت خروجی 25 را در رابط اینترنت روتر یا رابط ارائه دهنده میزبانی خود باز کنید. (ممکن است برخی از ارائه دهندگان میزبانی از شما بخواهند که برای این کار تیکت پشتیبانی ارسال کنید).", + "diagnosis_mail_outgoing_port_25_blocked": "سرور ایمیل SMTP نمی تواند به سرورهای دیگر ایمیل ارسال کند زیرا درگاه خروجی 25 در IPv {ipversion} مسدود شده است.", + "diagnosis_mail_outgoing_port_25_ok": "سرور ایمیل SMTP قادر به ارسال ایمیل است (پورت خروجی 25 مسدود نشده است).", + "diagnosis_swap_tip": "لطفاً مراقب و آگاه باشید، اگر سرور میزبانی swap را روی کارت SD یا حافظه SSD انجام دهد ، ممکن است طول عمر دستگاه را به شدت کاهش دهد.", + "diagnosis_swap_ok": "سیستم {total} swap دارد!", + "diagnosis_swap_notsomuch": "سیستم فقط {total} swap دارد. برای جلوگیری از شرایطی که حافظه سیستم شما تمام می شود ، باید حداقل {recommended} را در نظر بگیرید.", + "diagnosis_swap_none": "این سیستم به هیچ وجه swap ندارد. برای جلوگیری از شرایطی که حافظه سیستم شما تمام می شود ، باید حداقل {recommended} swap را در نظر بگیرید.", + "diagnosis_ram_ok": "این سیستم هنوز {available} ({available_percent}٪) حافظه در دسترس دارد از مجموع {total}.", + "diagnosis_ram_low": "این سیستم فقط {available} ({available_percent}٪) حافظه در دسترس دارد! (از {total}). مراقب باشید.", + "diagnosis_ram_verylow": "این سیستم فقط {available} ({available_percent}٪) حافظه در دسترس دارد! (از {total})", + "diagnosis_diskusage_ok": "‏ذخیره سازی {mountpoint} (روی دستگاه {device}) هنوز {free} فضا در دسترس دارد ({free_percent}%) فضای باقی مانده (از {total})!" } From 2f944d6258c68997482d3dfa180995ceb810cb0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M?= Date: Wed, 1 Sep 2021 04:04:55 +0000 Subject: [PATCH 106/114] Translated using Weblate (Galician) Currently translated at 100.0% (643 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/gl/ --- locales/gl.json | 65 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/locales/gl.json b/locales/gl.json index e752716cd..56204a9ea 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -481,9 +481,9 @@ "pattern_email_forward": "Ten que ser un enderezo de email válido, está aceptado o símbolo '+' (ex. persoa+etiqueta@exemplo.com)", "pattern_domain": "Ten que ser un nome de dominio válido (ex. dominiopropio.org)", "pattern_backup_archive_name": "Ten que ser un nome de ficheiro válido con 30 caracteres como máximo, alfanuméricos ou só caracteres -_.", - "password_too_simple_4": "O contrasinal debe ter 12 caracteres como mínimo e conter un díxito, maiúsculas, minúsculas e caracteres especiais", - "password_too_simple_3": "O contrasinal debe ter 8 caracteres como mínimo e conter un díxito, maiúsculas, minúsculas e caracteres especiais", - "password_too_simple_2": "O contrasinal debe ter 8 caracteres como mínimo e conter un díxito, maiúsculas e minúsculas", + "password_too_simple_4": "O contrasinal ten que ter 12 caracteres como mínimo e conter un díxito, maiúsculas, minúsculas e caracteres especiais", + "password_too_simple_3": "O contrasinal ten que ter 8 caracteres como mínimo e conter un díxito, maiúsculas, minúsculas e caracteres especiais", + "password_too_simple_2": "O contrasinal ten que ter 8 caracteres como mínimo e conter un díxito, maiúsculas e minúsculas", "password_listed": "Este contrasinal está entre os máis utilizados no mundo. Por favor elixe outro que sexa máis orixinal.", "packages_upgrade_failed": "Non se puideron actualizar tódolos paquetes", "operation_interrupted": "Foi interrumpida manualmente a operación?", @@ -584,5 +584,62 @@ "invalid_password": "Contrasinal non válido", "ldap_server_is_down_restart_it": "O servidor LDAP está caído, intenta reinicialo...", "ldap_server_down": "Non se chegou ao servidor LDAP", - "global_settings_setting_security_experimental_enabled": "Activar características de seguridade experimentais (non actives isto se non sabes o que estás a facer!)" + "global_settings_setting_security_experimental_enabled": "Activar características de seguridade experimentais (non actives isto se non sabes o que estás a facer!)", + "yunohost_postinstall_end_tip": "Post-install completada! Para rematar a configuración considera:\n- engadir unha primeira usuaria na sección 'Usuarias' na webadmin (ou 'yunohost user create ' na liña de comandos);\n- diagnosticar potenciais problemas na sección 'Diagnóstico' na webadmin (ou 'yunohost diagnosis run' na liña de comandos);\n- ler 'Rematando a configuración' e 'Coñece YunoHost' na documentación da administración: https://yunohost.org/admindoc.", + "yunohost_not_installed": "YunoHost non está instalado correctamente. Executa 'yunohost tools postinstall'", + "yunohost_installing": "Instalando YunoHost...", + "yunohost_configured": "YunoHost está configurado", + "yunohost_already_installed": "YunoHost xa está instalado", + "user_updated": "Cambiada a info da usuaria", + "user_update_failed": "Non se actualizou usuaria {user}: {error}", + "user_unknown": "Usuaria descoñecida: {user}", + "user_home_creation_failed": "Non se puido crear cartafol 'home' para a usuaria", + "user_deletion_failed": "Non se puido eliminar a usuaria {user}: {error}", + "user_deleted": "Usuaria eliminada", + "user_creation_failed": "Non se puido crear a usuaria {user}: {error}", + "user_created": "Usuaria creada", + "user_already_exists": "A usuaria '{user}' xa existe", + "upnp_port_open_failed": "Non se puido abrir porto a través de UPnP", + "upnp_dev_not_found": "Non se atopa dispositivo UPnP", + "upgrading_packages": "Actualizando paquetes...", + "upgrade_complete": "Actualización completa", + "updating_apt_cache": "Obtendo actualizacións dispoñibles para os paquetes do sistema...", + "update_apt_cache_warning": "Algo fallou ao actualizar a caché de APT (xestor de paquetes Debian). Aquí tes un volcado de sources.list, que podería axudar a identificar liñas problemáticas:\n{sourceslist}", + "update_apt_cache_failed": "Non se puido actualizar a caché de APT (xestor de paquetes de Debian). Aquí tes un volcado do sources.list, que podería axudarche a identificar liñas incorrectas:\n{sourceslist}", + "unrestore_app": "{app} non vai ser restablecida", + "unlimit": "Sen cota", + "unknown_main_domain_path": "Dominio ou ruta descoñecida '{app}'. Tes que indicar un dominio e ruta para poder especificar un URL para o permiso.", + "unexpected_error": "Aconteceu un fallo non agardado: {error}", + "unbackup_app": "{app} non vai ser gardada", + "tools_upgrade_special_packages_completed": "Completada a actualización dos paquetes YunoHost.\nPreme [Enter] para recuperar a liña de comandos", + "tools_upgrade_special_packages_explanation": "A actualización especial continuará en segundo plano. Non inicies outras tarefas no servidor nos seguintes ~10 minutos (depende do hardware). Após isto, podes volver a conectar na webadmin. O rexistro da actualización estará dispoñible en Ferramentas → Rexistro (na webadmin) ou con 'yunohost log list' (na liña de comandos).", + "tools_upgrade_special_packages": "Actualizando paquetes 'special' (yunohost-related)…", + "tools_upgrade_regular_packages_failed": "Non se actualizaron os paquetes: {packages_list}", + "tools_upgrade_regular_packages": "Actualizando os paquetes 'regular' (non-yunohost-related)…", + "tools_upgrade_cant_unhold_critical_packages": "Non se desbloquearon os paquetes críticos…", + "tools_upgrade_cant_hold_critical_packages": "Non se puideron bloquear os paquetes críticos…", + "tools_upgrade_cant_both": "Non se pode actualizar o sistema e as apps ao mesmo tempo", + "tools_upgrade_at_least_one": "Por favor indica 'apps', ou 'system'", + "this_action_broke_dpkg": "Esta acción rachou dpkg/APT (xestores de paquetes do sistema)... Podes intentar resolver o problema conectando a través de SSH e executando `sudo apt install --fix-broken`e/ou `sudo dpkg --configure -a`.", + "system_username_exists": "Xa existe este nome de usuaria na lista de usuarias do sistema", + "system_upgraded": "Sistema actualizado", + "ssowat_conf_updated": "Actualizada a configuración SSOwat", + "ssowat_conf_generated": "Rexenerada a configuración para SSOwat", + "show_tile_cant_be_enabled_for_regex": "Non podes activar 'show_tile' neste intre, porque o URL para o permiso '{permission}' é un regex", + "show_tile_cant_be_enabled_for_url_not_defined": "Non podes activar 'show_tile' neste intre, primeiro tes que definir un URL para o permiso '{permission}'", + "service_unknown": "Servizo descoñecido '{service}'", + "service_stopped": "Detívose o servizo '{service}'", + "service_stop_failed": "Non se puido deter o servizo '{service}'\n\nRexistros recentes do servizo: {logs}", + "service_started": "Iniciado o servizo '{service}'", + "service_start_failed": "Non se puido iniciar o servizo '{service}'\n\nRexistros recentes do servizo: {logs}", + "service_reloaded_or_restarted": "O servizo '{service}' foi recargado ou reiniciado", + "service_reload_or_restart_failed": "Non se recargou ou reiniciou o servizo '{service}'\n\nRexistros recentes do servizo: {logs}", + "service_restarted": "Reiniciado o servizo '{service}'", + "service_restart_failed": "Non se reiniciou o servizo '{service}'\n\nRexistros recentes do servizo: {logs}", + "service_reloaded": "Recargado o servizo '{service}'", + "service_reload_failed": "Non se recargou o servizo '{service}'\n\nRexistros recentes do servizo: {logs}", + "service_removed": "Eliminado o servizo '{service}'", + "service_remove_failed": "Non se eliminou o servizo '{service}'", + "service_regen_conf_is_deprecated": "'yunohost service regen-conf' xa non se utiliza! Executa 'yunohost tools regen-conf' no seu lugar.", + "service_enabled": "O servizo '{service}' vai ser iniciado automáticamente no inicio do sistema." } From 868c9c7f78eab6c09a34d6380275ec563d2150d5 Mon Sep 17 00:00:00 2001 From: Parviz Homayun Date: Wed, 1 Sep 2021 05:40:50 +0000 Subject: [PATCH 107/114] Translated using Weblate (Persian) Currently translated at 41.8% (269 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fa/ --- locales/fa.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/locales/fa.json b/locales/fa.json index c744a8637..18db28fdc 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -263,5 +263,9 @@ "diagnosis_ram_ok": "این سیستم هنوز {available} ({available_percent}٪) حافظه در دسترس دارد از مجموع {total}.", "diagnosis_ram_low": "این سیستم فقط {available} ({available_percent}٪) حافظه در دسترس دارد! (از {total}). مراقب باشید.", "diagnosis_ram_verylow": "این سیستم فقط {available} ({available_percent}٪) حافظه در دسترس دارد! (از {total})", - "diagnosis_diskusage_ok": "‏ذخیره سازی {mountpoint} (روی دستگاه {device}) هنوز {free} فضا در دسترس دارد ({free_percent}%) فضای باقی مانده (از {total})!" + "diagnosis_diskusage_ok": "‏ذخیره سازی {mountpoint} (روی دستگاه {device}) هنوز {free} فضا در دسترس دارد ({free_percent}%) فضای باقی مانده (از {total})!", + "diagnosis_http_nginx_conf_not_up_to_date": "به نظر می رسد که پیکربندی nginx این دامنه به صورت دستی تغییر کرده است و از تشخیص YunoHost در صورت دسترسی به HTTP جلوگیری می کند.", + "diagnosis_http_partially_unreachable": "به نظر می رسد که دامنه {domain} از طریق HTTP از خارج از شبکه محلی در IPv{failed} غیرقابل دسترسی است، اگرچه در IPv{passed} کار می کند.", + "diagnosis_http_unreachable": "به نظر می رسد دامنه {domain} از خارج از شبکه محلی از طریق HTTP قابل دسترسی نیست.", + "diagnosis_http_bad_status_code": "به نظر می رسد دستگاه دیگری (شاید روتر اینترنتی شما) به جای سرور شما پاسخ داده است.
1. شایع ترین علت برای این مشکل ، پورت 80 است (و 443) به درستی به سرور شما ارسال نمی شوند.
2. در تنظیمات پیچیده تر: مطمئن شوید که هیچ فایروال یا پروکسی معکوسی تداخل نداشته باشد." } From 10b813b6bb285f34e60be897ddeb58ecbbb6c961 Mon Sep 17 00:00:00 2001 From: Parviz Homayun Date: Wed, 1 Sep 2021 06:14:39 +0000 Subject: [PATCH 108/114] Translated using Weblate (Persian) Currently translated at 43.2% (278 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fa/ --- locales/fa.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/locales/fa.json b/locales/fa.json index 18db28fdc..2e943fe31 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -267,5 +267,14 @@ "diagnosis_http_nginx_conf_not_up_to_date": "به نظر می رسد که پیکربندی nginx این دامنه به صورت دستی تغییر کرده است و از تشخیص YunoHost در صورت دسترسی به HTTP جلوگیری می کند.", "diagnosis_http_partially_unreachable": "به نظر می رسد که دامنه {domain} از طریق HTTP از خارج از شبکه محلی در IPv{failed} غیرقابل دسترسی است، اگرچه در IPv{passed} کار می کند.", "diagnosis_http_unreachable": "به نظر می رسد دامنه {domain} از خارج از شبکه محلی از طریق HTTP قابل دسترسی نیست.", - "diagnosis_http_bad_status_code": "به نظر می رسد دستگاه دیگری (شاید روتر اینترنتی شما) به جای سرور شما پاسخ داده است.
1. شایع ترین علت برای این مشکل ، پورت 80 است (و 443) به درستی به سرور شما ارسال نمی شوند.
2. در تنظیمات پیچیده تر: مطمئن شوید که هیچ فایروال یا پروکسی معکوسی تداخل نداشته باشد." + "diagnosis_http_bad_status_code": "به نظر می رسد دستگاه دیگری (شاید روتر اینترنتی شما) به جای سرور شما پاسخ داده است.
1. شایع ترین علت برای این مشکل ، پورت 80 است (و 443) به درستی به سرور شما ارسال نمی شوند.
2. در تنظیمات پیچیده تر: مطمئن شوید که هیچ فایروال یا پروکسی معکوسی تداخل نداشته باشد.", + "disk_space_not_sufficient_update": "برای به روزرسانی این برنامه فضای دیسک کافی باقی نمانده است", + "disk_space_not_sufficient_install": "فضای کافی برای نصب این برنامه در دیسک باقی نمانده است", + "diagnosis_sshd_config_inconsistent_details": "لطفاً اجراکنید yunohost settings set security.ssh.port -v YOUR_SSH_PORT برای تعریف پورت SSH و بررسی کنید yunohost tools regen-conf ssh --dry-run --with-diff و yunohost tools regen-conf ssh --force برای تنظیم مجدد تنظیمات خود به توصیه YunoHost.", + "diagnosis_sshd_config_inconsistent": "به نظر می رسد که پورت SSH به صورت دستی در/etc/ssh/sshd_config تغییر یافته است. از زمان YunoHost 4.2 ، یک تنظیم جهانی جدید 'security.ssh.port' برای جلوگیری از ویرایش دستی پیکربندی در دسترس است.", + "diagnosis_sshd_config_insecure": "به نظر می رسد که پیکربندی SSH به صورت دستی تغییر یافته است و مطمئن نیست زیرا هیچ دستورالعمل 'AllowGroups' یا 'AllowUsers' برای محدود کردن دسترسی به کاربران مجاز ندارد.", + "diagnosis_processes_killed_by_oom_reaper": "برخی از فرآیندها اخیراً توسط سیستم از بین رفته اند زیرا حافظه آن تمام شده است. این به طور معمول نشانه کمبود حافظه در سیستم یا فرآیندی است که حافظه زیادی را از بین می برد. خلاصه فرآیندهای کشته شده:\n{kills_summary}", + "diagnosis_never_ran_yet": "به نظر می رسد این سرور به تازگی راه اندازی شده است و هنوز هیچ گزارش تشخیصی برای نمایش وجود ندارد. شما باید با اجرای یک عیب یابی و تشخیص کامل، از طریق رابط مدیریت تحت وب webadmin یا با استفاده از 'yunohost diagnosis run' از خط فرمان معاینه و تشخیص عیب یابی را شروع کنید.", + "diagnosis_unknown_categories": "دسته های زیر ناشناخته است: {categories}", + "diagnosis_http_nginx_conf_not_up_to_date_details": "برای برطرف کردن وضعیّت ، تفاوت را با استفاده از خط فرمان بررسی کنیدyunohost tools regen-conf nginx --dry-run --with-diff و اگر خوب است ، تغییرات را اعمال کنید با استفاده از فرمان yunohost tools regen-conf nginx --force." } From d9afb8507bc9ba37649cb2f1ceb36358a3e779e7 Mon Sep 17 00:00:00 2001 From: Parviz Homayun Date: Wed, 1 Sep 2021 06:37:42 +0000 Subject: [PATCH 109/114] Translated using Weblate (Persian) Currently translated at 43.7% (281 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fa/ --- locales/fa.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/locales/fa.json b/locales/fa.json index 2e943fe31..19536c9c7 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -276,5 +276,8 @@ "diagnosis_processes_killed_by_oom_reaper": "برخی از فرآیندها اخیراً توسط سیستم از بین رفته اند زیرا حافظه آن تمام شده است. این به طور معمول نشانه کمبود حافظه در سیستم یا فرآیندی است که حافظه زیادی را از بین می برد. خلاصه فرآیندهای کشته شده:\n{kills_summary}", "diagnosis_never_ran_yet": "به نظر می رسد این سرور به تازگی راه اندازی شده است و هنوز هیچ گزارش تشخیصی برای نمایش وجود ندارد. شما باید با اجرای یک عیب یابی و تشخیص کامل، از طریق رابط مدیریت تحت وب webadmin یا با استفاده از 'yunohost diagnosis run' از خط فرمان معاینه و تشخیص عیب یابی را شروع کنید.", "diagnosis_unknown_categories": "دسته های زیر ناشناخته است: {categories}", - "diagnosis_http_nginx_conf_not_up_to_date_details": "برای برطرف کردن وضعیّت ، تفاوت را با استفاده از خط فرمان بررسی کنیدyunohost tools regen-conf nginx --dry-run --with-diff و اگر خوب است ، تغییرات را اعمال کنید با استفاده از فرمان yunohost tools regen-conf nginx --force." + "diagnosis_http_nginx_conf_not_up_to_date_details": "برای برطرف کردن وضعیّت ، تفاوت را با استفاده از خط فرمان بررسی کنیدyunohost tools regen-conf nginx --dry-run --with-diff و اگر خوب است ، تغییرات را اعمال کنید با استفاده از فرمان yunohost tools regen-conf nginx --force.", + "domain_cannot_remove_main_add_new_one": "شما نمی توانید '{domain}' را حذف کنید زیرا دامنه اصلی و تنها دامنه شما است، ابتدا باید دامنه دیگری را با 'yunohost domain add ' اضافه کنید، سپس با استفاده از 'yunohost domain main-domain -n ' به عنوان دامنه اصلی تنظیم شده. و بعد از آن می توانید با استفاده از' yunohost domain remove '{domain}''دامنه '{domain}'را حذف کنید.", + "domain_cannot_add_xmpp_upload": "شما نمی توانید دامنه هایی را که با \"xmpp-upload\" شروع می شوند اضافه کنید. این نوع نام مختص ویژگی بارگذاری XMPP است که در YunoHost یکپارچه شده است.", + "domain_cannot_remove_main": "شما نمی توانید '{domain}' را حذف کنید زیرا دامنه اصلی است ، ابتدا باید با استفاده از 'yunohost domain main-domain -n ' دامنه دیگری را به عنوان دامنه اصلی تعیین کنید. در اینجا لیست دامنه های کاندید وجود دارد: {other_domains}" } From 8d7eaae1f0124df433c940c1f3eb5ed74a94c445 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 1 Sep 2021 11:32:21 +0200 Subject: [PATCH 110/114] Typos in french strings --- locales/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 0b5a5e93f..9d382304d 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -571,7 +571,7 @@ "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 yunohost dyndns update --force.", "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}", - "global_settings_setting_backup_compress_tar_archives": "Lors de la création de nouvelles sauvegardes, compressez les archives (.tar.gz) au lieu des archives non compressées (.tar). N.B. : activer cette option permet de créer des archives plus légères, mais la procédure de sauvegarde initiale sera significativement plus longues et plus gourmandes en CPU.", + "global_settings_setting_backup_compress_tar_archives": "Lors de la création de nouvelles sauvegardes, compresser automatiquement les archives (.tar.gz) au lieu des archives non compressées (.tar). N.B. : activer cette option permet de créer des archives plus légères, mais la procédure de sauvegarde initiale sera significativement plus longues et plus gourmandes en CPU.", "migration_description_0018_xtable_to_nftable": "Migrer les anciennes règles de trafic réseau vers le nouveau système basé sur nftables", "service_description_php7.3-fpm": "Exécute les applications écrites en PHP avec NGINX", "migration_0018_failed_to_reset_legacy_rules": "La réinitialisation des règles iptable par défaut a échoué : {error}", @@ -641,5 +641,5 @@ "invalid_password": "Mot de passe incorrect", "ldap_server_is_down_restart_it": "Le service LDAP est en panne, essayez de le redémarrer...", "ldap_server_down": "Impossible d'atteindre le serveur LDAP", - "global_settings_setting_security_experimental_enabled": "Activez les fonctionnalités de sécurité expérimentales (ne l'activez pas si vous ne savez pas ce que vous faites !)" + "global_settings_setting_security_experimental_enabled": "Activer les fonctionnalités de sécurité expérimentales (ne l'activez pas si vous ne savez pas ce que vous faites !)" } From e30063743a02c8eceb97bdb3b0d45ccf33c2a46a Mon Sep 17 00:00:00 2001 From: Parviz Homayun Date: Wed, 1 Sep 2021 09:25:20 +0000 Subject: [PATCH 111/114] Translated using Weblate (Persian) Currently translated at 57.5% (370 of 643 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fa/ --- locales/fa.json | 93 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/locales/fa.json b/locales/fa.json index 19536c9c7..31a24e269 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -277,7 +277,96 @@ "diagnosis_never_ran_yet": "به نظر می رسد این سرور به تازگی راه اندازی شده است و هنوز هیچ گزارش تشخیصی برای نمایش وجود ندارد. شما باید با اجرای یک عیب یابی و تشخیص کامل، از طریق رابط مدیریت تحت وب webadmin یا با استفاده از 'yunohost diagnosis run' از خط فرمان معاینه و تشخیص عیب یابی را شروع کنید.", "diagnosis_unknown_categories": "دسته های زیر ناشناخته است: {categories}", "diagnosis_http_nginx_conf_not_up_to_date_details": "برای برطرف کردن وضعیّت ، تفاوت را با استفاده از خط فرمان بررسی کنیدyunohost tools regen-conf nginx --dry-run --with-diff و اگر خوب است ، تغییرات را اعمال کنید با استفاده از فرمان yunohost tools regen-conf nginx --force.", - "domain_cannot_remove_main_add_new_one": "شما نمی توانید '{domain}' را حذف کنید زیرا دامنه اصلی و تنها دامنه شما است، ابتدا باید دامنه دیگری را با 'yunohost domain add ' اضافه کنید، سپس با استفاده از 'yunohost domain main-domain -n ' به عنوان دامنه اصلی تنظیم شده. و بعد از آن می توانید با استفاده از' yunohost domain remove '{domain}''دامنه '{domain}'را حذف کنید.", + "domain_cannot_remove_main_add_new_one": "شما نمی توانید '{domain}' را حذف کنید زیرا دامنه اصلی و تنها دامنه شما است، ابتدا باید دامنه دیگری را با 'yunohost domain add ' اضافه کنید، سپس با استفاده از 'yunohost domain main-domain -n ' به عنوان دامنه اصلی تنظیم شده. و بعد از آن می توانید'{domain}' را حذف کنید با استفاده از'yunohost domain remove {domain}'.'", "domain_cannot_add_xmpp_upload": "شما نمی توانید دامنه هایی را که با \"xmpp-upload\" شروع می شوند اضافه کنید. این نوع نام مختص ویژگی بارگذاری XMPP است که در YunoHost یکپارچه شده است.", - "domain_cannot_remove_main": "شما نمی توانید '{domain}' را حذف کنید زیرا دامنه اصلی است ، ابتدا باید با استفاده از 'yunohost domain main-domain -n ' دامنه دیگری را به عنوان دامنه اصلی تعیین کنید. در اینجا لیست دامنه های کاندید وجود دارد: {other_domains}" + "domain_cannot_remove_main": "شما نمی توانید '{domain}' را حذف کنید زیرا دامنه اصلی است ، ابتدا باید با استفاده از 'yunohost domain main-domain -n ' دامنه دیگری را به عنوان دامنه اصلی تعیین کنید. در اینجا لیست دامنه های کاندید وجود دارد: {other_domains}", + "installation_complete": "نصب تکمیل شد", + "hook_name_unknown": "نام قلاب ناشناخته '{name}'", + "hook_list_by_invalid": "از این ویژگی نمی توان برای فهرست قلاب ها استفاده کرد", + "hook_json_return_error": "بازگشت از قلاب {path} خوانده نشد. خطا: {msg}. محتوای خام: {raw_content}", + "hook_exec_not_terminated": "اسکریپت به درستی به پایان نرسید: {path}", + "hook_exec_failed": "اسکریپت اجرا نشد: {path}", + "group_user_not_in_group": "کاربر {user} در گروه {group} نیست", + "group_user_already_in_group": "کاربر {user} در حال حاضر در گروه {group} است", + "group_update_failed": "گروه '{group}' به روز نشد: {error}", + "group_updated": "گروه '{group}' به روز شد", + "group_unknown": "گروه '{group}' ناشناخته است", + "group_deletion_failed": "گروه '{group}' حذف نشد: {error}", + "group_deleted": "گروه '{group}' حذف شد", + "group_cannot_be_deleted": "گروه {group} را نمی توان به صورت دستی حذف کرد.", + "group_cannot_edit_primary_group": "گروه '{group}' را نمی توان به صورت دستی ویرایش کرد. این گروه اصلی شامل تنها یک کاربر خاص است.", + "group_cannot_edit_visitors": "ویرایش گروه 'visitors' بازدیدکنندگان به صورت دستی امکان پذیر نیست. این گروه ویژه، نمایانگر بازدیدکنندگان ناشناس است", + "group_cannot_edit_all_users": "گروه 'all_users' را نمی توان به صورت دستی ویرایش کرد. این یک گروه ویژه است که شامل همه کاربران ثبت شده در YunoHost میباشد", + "group_creation_failed": "گروه '{group}' ایجاد نشد: {error}", + "group_created": "گروه '{group}' ایجاد شد", + "group_already_exist_on_system_but_removing_it": "گروه {group} از قبل در گروه های سیستم وجود دارد ، اما YunoHost آن را حذف می کند...", + "group_already_exist_on_system": "گروه {group} از قبل در گروه های سیستم وجود دارد", + "group_already_exist": "گروه {group} از قبل وجود دارد", + "good_practices_about_user_password": "گذرواژه باید حداقل 8 کاراکتر باشد - اگرچه استفاده از گذرواژه طولانی تر (به عنوان مثال عبارت عبور) و/یا استفاده از تنوع کاراکترها (بزرگ ، کوچک ، رقم و کاراکتر های خاص) تمرین خوبی است.", + "good_practices_about_admin_password": "اکنون می خواهید گذرواژه جدیدی برای مدیریت تعریف کنید. گذرواژه باید حداقل 8 کاراکتر باشد - اگرچه استفاده از گذرواژه طولانی تر (به عنوان مثال عبارت عبور) و/یا استفاده از تنوع کاراکترها (بزرگ ، کوچک ، رقم و کاراکتر های خاص) تمرین خوبی است.", + "global_settings_unknown_type": "وضعیت غیرمنتظره ، به نظر می رسد که تنظیمات {setting} دارای نوع {unknown_type} است اما از نوع پشتیبانی شده توسط سیستم نیست.", + "global_settings_setting_backup_compress_tar_archives": "هنگام ایجاد پشتیبان جدید ، بایگانی های فشرده (.tar.gz) را به جای بایگانی های فشرده نشده (.tar) انتخاب کنید. N.B. : فعال کردن این گزینه به معنای ایجاد آرشیوهای پشتیبان سبک تر است ، اما روش پشتیبان گیری اولیه به طور قابل توجهی طولانی تر و سنگین تر بر روی CPU خواهد بود.", + "global_settings_setting_security_experimental_enabled": "فعال کردن ویژگی های امنیتی آزمایشی (اگر نمی دانید در حال انجام چه کاری هستید این کار را انجام ندهید!)", + "global_settings_setting_security_webadmin_allowlist": "آدرس های IP که مجاز به دسترسی مدیر وب هستند. جدا شده با ویرگول.", + "global_settings_setting_security_webadmin_allowlist_enabled": "فقط به برخی از IP ها اجازه دسترسی به مدیریت وب را بدهید.", + "global_settings_setting_smtp_relay_password": "رمز عبور میزبان رله SMTP", + "global_settings_setting_smtp_relay_user": "حساب کاربری رله SMTP", + "global_settings_setting_smtp_relay_port": "پورت رله SMTP", + "global_settings_setting_smtp_relay_host": "میزبان رله SMTP برای ارسال نامه به جای این نمونه yunohost استفاده می شود. اگر در یکی از این شرایط قرار دارید مفید است: پورت 25 شما توسط ارائه دهنده ISP یا VPS شما مسدود شده است، شما یک IP مسکونی دارید که در DUHL ذکر شده است، نمی توانید DNS معکوس را پیکربندی کنید یا این سرور مستقیماً در اینترنت نمایش داده نمی شود و می خواهید از یکی دیگر برای ارسال ایمیل استفاده کنید.", + "global_settings_setting_smtp_allow_ipv6": "اجازه دهید از IPv6 برای دریافت و ارسال نامه استفاده شود", + "global_settings_setting_ssowat_panel_overlay_enabled": "همپوشانی پانل SSOwat را فعال کنید", + "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "اجازه دهید از کلید میزبان DSA (منسوخ شده) برای پیکربندی SH daemon استفاده شود", + "global_settings_unknown_setting_from_settings_file": "کلید ناشناخته در تنظیمات: '{setting_key}'، آن را کنار گذاشته و در /etc/yunohost/settings-unknown.json ذخیره کنید", + "global_settings_setting_security_ssh_port": "درگاه SSH", + "global_settings_setting_security_postfix_compatibility": "سازگاری در مقابل مبادله امنیتی برای سرور Postfix. روی رمزها (و سایر جنبه های مرتبط با امنیت) تأثیر می گذارد", + "global_settings_setting_security_ssh_compatibility": "سازگاری در مقابل مبادله امنیتی برای سرور SSH. روی رمزها (و سایر جنبه های مرتبط با امنیت) تأثیر می گذارد", + "global_settings_setting_security_password_user_strength": "قدرت رمز عبور کاربر", + "global_settings_setting_security_password_admin_strength": "قدرت رمز عبور مدیر", + "global_settings_setting_security_nginx_compatibility": "سازگاری در مقابل مبادله امنیتی برای وب سرور NGINX. روی رمزها (و سایر جنبه های مرتبط با امنیت) تأثیر می گذارد", + "global_settings_setting_pop3_enabled": "پروتکل POP3 را برای سرور ایمیل فعال کنید", + "global_settings_reset_success": "تنظیمات قبلی اکنون در {path} پشتیبان گیری شده است", + "global_settings_key_doesnt_exists": "کلید '{settings_key}' در تنظیمات جهانی وجود ندارد ، با اجرای 'لیست تنظیمات yunohost' می توانید همه کلیدهای موجود را مشاهده کنید", + "global_settings_cant_write_settings": "فایل تنظیمات ذخیره نشد، به دلیل: {reason}", + "global_settings_cant_serialize_settings": "سریال سازی داده های تنظیمات انجام نشد، به دلیل: {reason}", + "global_settings_cant_open_settings": "فایل تنظیمات باز نشد ، به دلیل: {reason}", + "global_settings_bad_type_for_setting": "نوع نادرست برای تنظیم {setting} ، دریافت شده {received_type}، مورد انتظار {expected_type}", + "global_settings_bad_choice_for_enum": "انتخاب نادرست برای تنظیم {setting} ، '{choice}' دریافت شد ، اما گزینه های موجود عبارتند از: {available_choices}", + "firewall_rules_cmd_failed": "برخی از دستورات قانون فایروال شکست خورده است. اطلاعات بیشتر در گزارش.", + "firewall_reloaded": "فایروال بارگیری مجدد شد", + "firewall_reload_failed": "بارگیری مجدد فایروال امکان پذیر نیست", + "file_does_not_exist": "فایل {path} وجود ندارد.", + "field_invalid": "فیلد نامعتبر '{}'", + "experimental_feature": "هشدار: این ویژگی آزمایشی است و پایدار تلقی نمی شود ، نباید از آن استفاده کنید مگر اینکه بدانید در حال انجام چه کاری هستید.", + "extracting": "استخراج...", + "dyndns_unavailable": "دامنه '{domain}' در دسترس نیست.", + "dyndns_domain_not_provided": "ارائه دهنده DynDNS {provider} نمی تواند دامنه {domain} را ارائه دهد.", + "dyndns_registration_failed": "دامنه DynDNS ثبت نشد: {error}", + "dyndns_registered": "دامنه DynDNS ثبت شد", + "dyndns_provider_unreachable": "دسترسی به ارائه دهنده DynDNS {provider} امکان پذیر نیست: یا YunoHost شما به درستی به اینترنت متصل نیست یا سرور dynette خراب است.", + "dyndns_no_domain_registered": "هیچ دامنه ای با DynDNS ثبت نشده است", + "dyndns_key_not_found": "کلید DNS برای دامنه یافت نشد", + "dyndns_key_generating": "ایجاد کلید DNS... ممکن است مدتی طول بکشد.", + "dyndns_ip_updated": "IP خود را در DynDNS به روز کرد", + "dyndns_ip_update_failed": "آدرس IP را به DynDNS به روز نکرد", + "dyndns_could_not_check_available": "بررسی نشد که آیا {domain} در {provider} در دسترس است یا خیر.", + "dyndns_could_not_check_provide": "بررسی نشد که آیا {provider} می تواند {domain} را ارائه دهد یا خیر.", + "dpkg_lock_not_available": "این دستور در حال حاضر قابل اجرا نیست زیرا به نظر می رسد برنامه دیگری از قفل dpkg (مدیر بسته سیستم) استفاده می کند", + "dpkg_is_broken": "شما نمی توانید این کار را در حال حاضر انجام دهید زیرا dpkg/APT (اداره کنندگان سیستم بسته ها) به نظر می رسد در وضعیت خرابی است… می توانید با اتصال از طریق SSH و اجرا این فرمان `sudo apt install --fix-broken` and/or `sudo dpkg --configure -a` مشکل را حل کنید.", + "downloading": "در حال بارگیری…", + "done": "انجام شد", + "domains_available": "دامنه های موجود:", + "domain_unknown": "دامنه ناشناخته", + "domain_name_unknown": "دامنه '{domain}' ناشناخته است", + "domain_uninstall_app_first": "این برنامه ها هنوز روی دامنه شما نصب هستند:\n{apps}\n\nلطفاً قبل از اقدام به حذف دامنه ، آنها را با استفاده از 'برنامه yunohost remove the_app_id' حذف کرده یا با استفاده از 'yunohost app change-url the_app_id' به دامنه دیگری منتقل کنید", + "domain_remove_confirm_apps_removal": "حذف این دامنه برنامه های زیر را حذف می کند:\n{apps}\n\nآیا طمئن هستید که میخواهید انجام دهید؟ [{answers}]", + "domain_hostname_failed": "نام میزبان جدید قابل تنظیم نیست. این ممکن است بعداً مشکلی ایجاد کند (ممکن هم هست خوب باشد).", + "domain_exists": "دامنه از قبل وجود دارد", + "domain_dyndns_root_unknown": "دامنه ریشه DynDNS ناشناخته", + "domain_dyndns_already_subscribed": "شما قبلاً در یک دامنه DynDNS مشترک شده اید", + "domain_dns_conf_is_just_a_recommendation": "این دستور پیکربندی * توصیه شده * را به شما نشان می دهد. در واقع پیکربندی DNS را برای شما تنظیم نمی کند. این وظیفه شماست که مطابق این توصیه ، منطقه DNS خود را در ثبت کننده خود پیکربندی کنید.", + "domain_deletion_failed": "حذف دامنه {domain} امکان پذیر نیست: {error}", + "domain_deleted": "دامنه حذف شد", + "domain_creation_failed": "ایجاد دامنه {domain} امکان پذیر نیست: {error}", + "domain_created": "دامنه ایجاد شد", + "domain_cert_gen_failed": "گواهی تولید نشد" } From 5501556dffb359153b9c1073dd2e89364318122d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 1 Sep 2021 15:02:30 +0200 Subject: [PATCH 112/114] ci: fix test jobs for i18n keys --- .gitlab/ci/test.gitlab-ci.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitlab/ci/test.gitlab-ci.yml b/.gitlab/ci/test.gitlab-ci.yml index a9e14b6e4..e0e0e001a 100644 --- a/.gitlab/ci/test.gitlab-ci.yml +++ b/.gitlab/ci/test.gitlab-ci.yml @@ -53,15 +53,17 @@ full-tests: test-i18n-keys: extends: .test-stage script: - - python3 -m pytest tests tests/test_i18n_keys.py + - python3 -m pytest tests/test_i18n_keys.py only: changes: - - locales/* + - locales/en.json + - src/yunohost/*.py + - data/hooks/diagnosis/*.py test-translation-format-consistency: extends: .test-stage script: - - python3 -m pytest tests tests/test_translation_format_consistency.py + - python3 -m pytest tests/test_translation_format_consistency.py only: changes: - locales/* From e8a63f21be7d95b31530f51b95c6de0480964c81 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 1 Sep 2021 15:21:07 +0200 Subject: [PATCH 113/114] ci: push a remove-stale-strings only if there are some non-whitespace changes --- .gitlab/ci/translation.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/ci/translation.gitlab-ci.yml b/.gitlab/ci/translation.gitlab-ci.yml index eef57ca22..d7962436c 100644 --- a/.gitlab/ci/translation.gitlab-ci.yml +++ b/.gitlab/ci/translation.gitlab-ci.yml @@ -16,7 +16,7 @@ remove-stale-translated-strings: # create a local branch that will overwrite distant one - git checkout -b "ci-remove-stale-translated-strings-${CI_COMMIT_REF_NAME}" --no-track - python3 remove_stale_translated_strings.py - - '[ $(git diff | wc -l) != 0 ] || exit 0' # stop if there is nothing to commit + - '[ $(git diff -w | wc -l) != 0 ] || exit 0' # stop if there is nothing to commit - git commit -am "[CI] Remove stale translated strings" || true - git push -f origin "ci-remove-stale-translated-strings-${CI_COMMIT_REF_NAME}":"ci-remove-stale-translated-strings-${CI_COMMIT_REF_NAME}" - hub pull-request -m "[CI] Remove stale translated strings" -b Yunohost:dev -p || true # GITHUB_USER and GITHUB_TOKEN registered here https://gitlab.com/yunohost/yunohost/-/settings/ci_cd From fe9ca56f8810e567705a33a925142c94804ed216 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 1 Sep 2021 15:48:35 +0200 Subject: [PATCH 114/114] README: translation status: use horizontal display + only 'core' --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fa3c839c9..9fc93740d 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Webadmin ([Yunohost-Admin](https://github.com/YunoHost/yunohost-admin)) | Single - You can help translate YunoHost on our [translation platform](https://translate.yunohost.org/engage/yunohost/?utm_source=widget)

-Translation status +Translation status

## License