diff --git a/bin/yunohost b/bin/yunohost
index fd9c2dbfd..10a21a9da 100755
--- a/bin/yunohost
+++ b/bin/yunohost
@@ -195,7 +195,8 @@ if __name__ == '__main__':
# Check that YunoHost is installed
if not os.path.isfile('/etc/yunohost/installed') and \
(len(args) < 2 or (args[0] +' '+ args[1] != 'tools postinstall' and \
- args[0] +' '+ args[1] != 'backup restore')):
+ args[0] +' '+ args[1] != 'backup restore' and \
+ args[0] +' '+ args[1] != 'log display')):
from moulinette import m18n
# Init i18n
diff --git a/bin/yunohost-api b/bin/yunohost-api
index 93d44c256..e518c34b0 100755
--- a/bin/yunohost-api
+++ b/bin/yunohost-api
@@ -149,6 +149,11 @@ def _init_moulinette(use_websocket=True, debug=False, verbose=False):
'handlers': [],
'propagate': True,
},
+ 'gnupg': {
+ 'level': 'INFO',
+ 'handlers': [],
+ 'propagate': False,
+ },
},
'root': {
'level': level,
@@ -196,12 +201,10 @@ if __name__ == '__main__':
_init_moulinette(opts.use_websocket, opts.debug, opts.verbose)
# Run the server
- from yunohost.utils.packages import ynh_packages_version
ret = moulinette.api(
_retrieve_namespaces(),
host=opts.host, port=opts.port, routes={
('GET', '/installed'): is_installed,
- ('GET', '/version'): ynh_packages_version,
}, use_cache=opts.use_cache, use_websocket=opts.use_websocket
)
sys.exit(ret)
diff --git a/bin/yunoprompt b/bin/yunoprompt
index 2b2a6cfb2..09400639b 100755
--- a/bin/yunoprompt
+++ b/bin/yunoprompt
@@ -1,8 +1,5 @@
#!/bin/bash
-# Fetch ips
-ip=$(hostname --all-ip-address)
-
# Fetch SSH fingerprints
i=0
for key in $(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key.pub 2> /dev/null) ; do
@@ -32,11 +29,17 @@ EOF
# Build the actual message
#
+sleep 5
+# Get local IP
+# (we do this after the sleep 5 to have
+# better chances that the network is up)
+local_ip=$(hostname --all-ip-address | awk '{print $1}')
+
LOGO_AND_FINGERPRINTS=$(cat << EOF
$LOGO
- IP: ${ip}
+ IP: ${local_ip}
SSH fingerprints:
${fingerprint[0]}
${fingerprint[1]}
@@ -51,17 +54,35 @@ if [[ -f /etc/yunohost/installed ]]
then
echo "$LOGO_AND_FINGERPRINTS" > /etc/issue
else
- sleep 5
chvt 2
+
+ # Formatting
+ [[ -n "$local_ip" ]] && local_ip=$(echo -e "https://$local_ip/") || local_ip="(no ip detected?)"
+
echo "$LOGO_AND_FINGERPRINTS"
- echo -e "\e[m Post-installation \e[0m"
- echo "Congratulations! YunoHost has been successfully installed.\nTwo more steps are required to activate the services of your server."
- read -p "Proceed to post-installation? (y/n)\nAlternatively, you can proceed the post-installation on https://${ip}" -n 1
+ cat << EOF
+===============================================================================
+You should now proceed with Yunohost post-installation. This is where you will
+be asked for :
+ - the main domain of your server ;
+ - the administration password.
+
+You can perform this step :
+ - from your web browser, by accessing : ${local_ip}
+ - or in this terminal by answering 'yes' to the following question
+
+If this is your first time with YunoHost, it is strongly recommended to take
+time to read the administator documentation and in particular the sections
+'Finalizing your setup' and 'Getting to know YunoHost'. It is available at
+the following URL : https://yunohost.org/admindoc
+===============================================================================
+EOF
+
+ read -p "Proceed with post-installation? (y/n) "
RESULT=1
while [ $RESULT -gt 0 ]; do
if [[ $REPLY =~ ^[Nn]$ ]]; then
- chvt 1
- exit 0
+ break
fi
echo -e "\n"
/usr/bin/yunohost tools postinstall
@@ -71,4 +92,6 @@ else
read -p "Retry? (y/n) " -n 1
fi
done
+ chvt 1
+ exit 0
fi
diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml
index 9f1652335..5bbd6e0dd 100644
--- a/data/actionsmap/yunohost.yml
+++ b/data/actionsmap/yunohost.yml
@@ -43,13 +43,8 @@ _global:
parameters:
uri: ldap://localhost:389
base_dn: dc=yunohost,dc=org
- user_rdn: cn=admin
- ldap-anonymous:
- vendor: ldap
- parameters:
- uri: ldap://localhost:389
- base_dn: dc=yunohost,dc=org
- argument_auth: true
+ user_rdn: cn=admin,dc=yunohost,dc=org
+ argument_auth: false
arguments:
-v:
full: --version
@@ -63,16 +58,13 @@ _global:
# User #
#############################
user:
- category_help: Manage users
+ category_help: Manage users and groups
actions:
### user_list()
list:
action_help: List users
api: GET /users
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
--fields:
help: fields to fetch
@@ -82,8 +74,6 @@ user:
create:
action_help: Create user
api: POST /users
- configuration:
- authenticate: all
arguments:
username:
help: The unique username to create
@@ -140,8 +130,6 @@ user:
delete:
action_help: Delete user
api: DELETE /users/
- configuration:
- authenticate: all
arguments:
username:
help: Username to delete
@@ -155,8 +143,6 @@ user:
update:
action_help: Update user informations
api: PUT /users/
- configuration:
- authenticate: all
arguments:
username:
help: Username to update
@@ -209,14 +195,177 @@ user:
info:
action_help: Get user information
api: GET /users/
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
username:
help: Username or email to get information
subcategories:
+ group:
+ subcategory_help: Manage group
+ actions:
+ ### user_group_list()
+ list:
+ action_help: List group
+ api: GET /users/groups
+ arguments:
+ --fields:
+ help: fields to fetch
+ nargs: "+"
+
+ ### user_group_add()
+ add:
+ action_help: Create group
+ api: POST /users/groups
+ arguments:
+ groupname:
+ help: The unique group name to add
+ extra:
+ pattern: &pattern_groupname
+ - !!str ^[a-z0-9_]+$
+ - "pattern_groupname"
+
+ ### user_group_delete()
+ delete:
+ action_help: Delete group
+ api: DELETE /users/groups/
+ arguments:
+ groupname:
+ help: Username to delete
+ extra:
+ pattern: *pattern_groupname
+
+ ### user_group_update()
+ update:
+ action_help: Update group
+ api: PUT /users/groups/
+ arguments:
+ groupname:
+ help: Username to update
+ extra:
+ pattern: *pattern_groupname
+ -a:
+ full: --add-user
+ help: User to add in group
+ nargs: "*"
+ metavar: USERNAME
+ extra:
+ pattern: *pattern_username
+ -r:
+ full: --remove-user
+ help: User to remove in group
+ nargs: "*"
+ metavar: USERNAME
+ extra:
+ pattern: *pattern_username
+
+ ### user_group_info()
+ info:
+ action_help: Get group information
+ api: GET /users/groups/
+ arguments:
+ groupname:
+ help: Groupname to get information
+ extra:
+ pattern: *pattern_username
+
+ permission:
+ subcategory_help: Manage user permission
+ actions:
+ ### user_permission_list()
+ list:
+ action_help: List access to user and group
+ api: GET /users/permission/
+ arguments:
+ -a:
+ full: --app
+ help: Application to manage the permission
+ nargs: "*"
+ metavar: APP
+ -p:
+ full: --permission
+ help: Name of permission (main by default)
+ nargs: "*"
+ metavar: PERMISSION
+ -u:
+ full: --username
+ help: Username
+ nargs: "*"
+ metavar: USER
+ -g:
+ full: --group
+ help: Group name
+ nargs: "*"
+ metavar: GROUP
+
+ ### user_permission_add()
+ add:
+ action_help: Grant access right to users and group
+ api: POST /users/permission/
+ arguments:
+ app:
+ help: Application to manage the permission
+ nargs: "+"
+ -p:
+ full: --permission
+ help: Name of permission (main by default)
+ nargs: "*"
+ metavar: PERMISSION
+ -u:
+ full: --username
+ help: Username
+ nargs: "*"
+ metavar: USER
+ extra:
+ pattern: *pattern_username
+ -g:
+ full: --group
+ help: Group name
+ nargs: "*"
+ metavar: GROUP
+ extra:
+ pattern: *pattern_username
+
+ ### user_permission_remove()
+ remove:
+ action_help: Revoke access right to users and group
+ api: PUT /users/permission/
+ arguments:
+ app:
+ help: Application to manage the permission
+ nargs: "+"
+ -p:
+ full: --permission
+ help: Name of permission (main by default)
+ nargs: "*"
+ metavar: PERMISSION
+ -u:
+ full: --username
+ help: Username
+ nargs: "*"
+ metavar: USER
+ extra:
+ pattern: *pattern_username
+ -g:
+ full: --group
+ help: Group name
+ nargs: "*"
+ metavar: GROUP
+ extra:
+ pattern: *pattern_username
+
+ ## user_permission_clear()
+ clear:
+ action_help: Reset access rights for the app
+ api: DELETE /users/permission/
+ arguments:
+ app:
+ help: Application to manage the permission
+ nargs: "+"
+ -p:
+ full: --permission
+ help: Name of permission (main by default)
+ nargs: "*"
+ metavar: PERMISSION
ssh:
subcategory_help: Manage ssh access
@@ -225,8 +374,6 @@ user:
allow:
action_help: Allow the user to uses ssh
api: POST /users/ssh/enable
- configuration:
- authenticate: all
arguments:
username:
help: Username of the user
@@ -237,8 +384,6 @@ user:
disallow:
action_help: Disallow the user to uses ssh
api: POST /users/ssh/disable
- configuration:
- authenticate: all
arguments:
username:
help: Username of the user
@@ -249,8 +394,6 @@ user:
list-keys:
action_help: Show user's authorized ssh keys
api: GET /users/ssh/keys
- configuration:
- authenticate: all
arguments:
username:
help: Username of the user
@@ -261,8 +404,6 @@ user:
add-key:
action_help: Add a new authorized ssh key for this user
api: POST /users/ssh/key
- configuration:
- authenticate: all
arguments:
username:
help: Username of the user
@@ -278,8 +419,6 @@ user:
remove-key:
action_help: Remove an authorized ssh key for this user
api: DELETE /users/ssh/key
- configuration:
- authenticate: all
arguments:
username:
help: Username of the user
@@ -300,16 +439,11 @@ domain:
list:
action_help: List domains
api: GET /domains
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
### domain_add()
add:
action_help: Create a custom domain
api: POST /domains
- configuration:
- authenticate: all
arguments:
domain:
help: Domain name to add
@@ -326,8 +460,6 @@ domain:
remove:
action_help: Delete domains
api: DELETE /domains/
- configuration:
- authenticate: all
arguments:
domain:
help: Domain to delete
@@ -338,9 +470,6 @@ domain:
dns-conf:
action_help: Generate DNS configuration for a domain
api: GET /domains//dns
- configuration:
- authenticate:
- - api
arguments:
domain:
help: Target domain
@@ -356,9 +485,6 @@ domain:
cert-status:
action_help: List status of current certificates (all by default).
api: GET /domains/cert-status/
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
domain_list:
help: Domains to check
@@ -371,9 +497,6 @@ domain:
cert-install:
action_help: Install Let's Encrypt certificates for given domains (all by default).
api: POST /domains/cert-install/
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
domain_list:
help: Domains for which to install the certificates
@@ -395,9 +518,6 @@ domain:
cert-renew:
action_help: Renew the Let's Encrypt certificates for given domains (all by default).
api: POST /domains/cert-renew/
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
domain_list:
help: Domains for which to renew the certificates
@@ -419,9 +539,6 @@ domain:
url-available:
action_help: Check availability of a web path
api: GET /domain/urlavailable
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
domain:
help: The domain for the web path (e.g. your.domain.tld)
@@ -542,9 +659,6 @@ app:
install:
action_help: Install apps
api: POST /apps
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
app:
help: Name, local path or git URL of the app to install
@@ -567,9 +681,6 @@ app:
remove:
action_help: Remove app
api: DELETE /apps/
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
app:
help: App(s) to delete
@@ -578,9 +689,6 @@ app:
upgrade:
action_help: Upgrade app
api: PUT /upgrade/apps
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
app:
help: App(s) to upgrade (default all)
@@ -596,9 +704,6 @@ app:
change-url:
action_help: Change app's URL
api: PUT /apps//changeurl
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
app:
help: Target app instance name
@@ -606,14 +711,14 @@ app:
full: --domain
help: New app domain on which the application will be moved
extra:
- ask: ask_main_domain
+ ask: ask_new_domain
pattern: *pattern_domain
required: True
-p:
full: --path
help: New path at which the application will be moved
extra:
- ask: ask_path
+ ask: ask_new_path
required: True
### app_setting()
@@ -651,9 +756,6 @@ app:
action_help: Check availability of a web path
api: GET /tools/checkurl
deprecated: True
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
url:
help: Url to check
@@ -665,9 +767,6 @@ app:
register-url:
action_help: Book/register a web path for a given app
api: PUT /tools/registerurl
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
app:
help: App which will use the web path
@@ -707,9 +806,6 @@ app:
makedefault:
action_help: Redirect domain root to an app
api: PUT /apps//default
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
app:
help: App name to put on domain root
@@ -721,17 +817,11 @@ app:
ssowatconf:
action_help: Regenerate SSOwat configuration file
api: PUT /ssowatconf
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
### app_change_label()
change-label:
action_help: Change app label
api: PUT /apps//label
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
app:
help: App ID
@@ -742,9 +832,6 @@ app:
addaccess:
action_help: Grant access right to users (everyone by default)
api: PUT /access
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
apps:
nargs: "+"
@@ -756,9 +843,6 @@ app:
removeaccess:
action_help: Revoke access right to users (everyone by default)
api: DELETE /access
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
apps:
nargs: "+"
@@ -770,9 +854,6 @@ app:
clearaccess:
action_help: Reset access rights for the app
api: POST /access
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
apps:
nargs: "+"
@@ -870,9 +951,6 @@ backup:
restore:
action_help: Restore from a local backup archive. If neither --apps or --system are given, this will restore all apps and all system parts in the archive. If only --apps if given, this will only restore apps and no system parts. Similarly, if only --system is given, this will only restore system parts and no apps.
api: POST /backup/restore/
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
name:
help: Name of the local backup archive
@@ -1244,6 +1322,14 @@ service:
-d:
full: --description
help: Description of the service
+ -t:
+ full: --log_type
+ help: Type of the log (file or systemd)
+ nargs: "+"
+ choices:
+ - file
+ - systemd
+ default: file
### service_remove()
remove:
@@ -1273,6 +1359,33 @@ service:
nargs: "+"
metavar: NAME
+ ### service_reload()
+ reload:
+ action_help: Reload one or more services
+ arguments:
+ names:
+ help: Service name to reload
+ nargs: "+"
+ metavar: NAME
+
+ ### service_restart()
+ restart:
+ action_help: Restart one or more services. If the services are not running yet, they will be started.
+ arguments:
+ names:
+ help: Service name to restart
+ nargs: "+"
+ metavar: NAME
+
+ ### service_reload_or_restart()
+ reload_or_restart:
+ action_help: Reload one or more services if they support it. If not, restart them instead. If the services are not running yet, they will be started.
+ arguments:
+ names:
+ help: Service name to reload or restart
+ nargs: "+"
+ metavar: NAME
+
### service_enable()
enable:
action_help: Enable one or more services
@@ -1537,19 +1650,10 @@ tools:
category_help: Specific tools
actions:
- ### tools_ldapinit()
- ldapinit:
- action_help: YunoHost LDAP initialization
- api: POST /ldap
- configuration:
- authenticate: all
-
### tools_adminpw()
adminpw:
action_help: Change password of admin and root users
api: PUT /adminpw
- configuration:
- authenticate: all
arguments:
-n:
full: --new-password
@@ -1565,8 +1669,6 @@ tools:
api:
- GET /domains/main
- PUT /domains/main
- configuration:
- authenticate: all
arguments:
-n:
full: --new-domain
@@ -1579,6 +1681,7 @@ tools:
action_help: YunoHost post-install
api: POST /postinstall
configuration:
+ # We need to be able to run the postinstall without being authenticated, otherwise we can't run the postinstall
authenticate: false
arguments:
-d:
@@ -1608,35 +1711,29 @@ tools:
action_help: YunoHost update
api: PUT /update
arguments:
- --ignore-apps:
- help: Ignore apps cache update and changelog
+ --apps:
+ help: Fetch the application list to check which apps can be upgraded
action: store_true
- --ignore-packages:
- help: Ignore APT cache update and changelog
+ --system:
+ help: Fetch available system packages upgrades (equivalent to apt update)
action: store_true
### tools_upgrade()
upgrade:
action_help: YunoHost upgrade
api: PUT /upgrade
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
- --ignore-apps:
- help: Ignore apps upgrade
- action: store_true
- --ignore-packages:
- help: Ignore APT packages upgrade
+ --apps:
+ help: List of apps to upgrade (all by default)
+ nargs: "*"
+ --system:
+ help: Upgrade only the system packages
action: store_true
### tools_diagnosis()
diagnosis:
action_help: YunoHost diagnosis
api: GET /diagnosis
- configuration:
- authenticate: all
- authenticator: ldap-anonymous
arguments:
-p:
full: --private
@@ -1655,8 +1752,6 @@ tools:
### tools_shell()
shell:
- configuration:
- authenticate: all
action_help: Launch a development shell
arguments:
-c:
@@ -1683,6 +1778,32 @@ tools:
full: --force
action: store_true
+ ### tools_regen_conf()
+ regen-conf:
+ action_help: Regenerate the configuration file(s)
+ api: PUT /tools/regenconf
+ arguments:
+ names:
+ help: Categories to regenerate configuration of (all by default)
+ nargs: "*"
+ metavar: NAME
+ -d:
+ full: --with-diff
+ help: Show differences in case of configuration changes
+ action: store_true
+ -f:
+ full: --force
+ help: Override all manual modifications in configuration files
+ action: store_true
+ -n:
+ full: --dry-run
+ help: Show what would have been regenerated
+ action: store_true
+ -p:
+ full: --list-pending
+ help: List pending configuration files and exit
+ action: store_true
+
subcategories:
migrations:
@@ -1843,6 +1964,10 @@ log:
full: --limit
help: Maximum number of logs
type: int
+ -d:
+ full: --with-details
+ help: Show additional infos (e.g. operation success) but may significantly increase command time. Consider using --limit in combination with this.
+ action: store_true
### log_display()
display:
diff --git a/data/actionsmap/yunohost_completion.py b/data/actionsmap/yunohost_completion.py
new file mode 100644
index 000000000..a4c17c4d6
--- /dev/null
+++ b/data/actionsmap/yunohost_completion.py
@@ -0,0 +1,86 @@
+"""
+Simple automated generation of a bash_completion file
+for yunohost command from the actionsmap.
+
+Generates a bash completion file assuming the structure
+`yunohost domain action`
+adds `--help` at the end if one presses [tab] again.
+
+author: Christophe Vuillot
+"""
+import os
+import yaml
+
+THIS_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+ACTIONSMAP_FILE = THIS_SCRIPT_DIR + '/yunohost.yml'
+BASH_COMPLETION_FILE = THIS_SCRIPT_DIR + '/../bash-completion.d/yunohost'
+
+with open(ACTIONSMAP_FILE, 'r') as stream:
+
+ # Getting the dictionary containning what actions are possible per domain
+ OPTION_TREE = yaml.load(stream)
+ DOMAINS = [str for str in OPTION_TREE.keys() if not str.startswith('_')]
+ DOMAINS_STR = '"{}"'.format(' '.join(DOMAINS))
+ ACTIONS_DICT = {}
+ for domain in DOMAINS:
+ ACTIONS = [str for str in OPTION_TREE[domain]['actions'].keys()
+ if not str.startswith('_')]
+ ACTIONS_STR = '"{}"'.format(' '.join(ACTIONS))
+ ACTIONS_DICT[domain] = ACTIONS_STR
+
+ with open(BASH_COMPLETION_FILE, 'w') as generated_file:
+
+ # header of the file
+ generated_file.write('#\n')
+ generated_file.write('# completion for yunohost\n')
+ generated_file.write('# automatically generated from the actionsmap\n')
+ generated_file.write('#\n\n')
+
+ # Start of the completion function
+ generated_file.write('_yunohost()\n')
+ generated_file.write('{\n')
+
+ # Defining local variable for previously and currently typed words
+ generated_file.write('\tlocal cur prev opts narg\n')
+ generated_file.write('\tCOMPREPLY=()\n\n')
+ generated_file.write('\t# the number of words already typed\n')
+ generated_file.write('\tnarg=${#COMP_WORDS[@]}\n\n')
+ generated_file.write('\t# the current word being typed\n')
+ generated_file.write('\tcur="${COMP_WORDS[COMP_CWORD]}"\n\n')
+ generated_file.write('\t# the last typed word\n')
+ generated_file.write('\tprev="${COMP_WORDS[COMP_CWORD-1]}"\n\n')
+
+ # If one is currently typing a domain then match with the domain list
+ generated_file.write('\t# If one is currently typing a domain,\n')
+ generated_file.write('\t# match with domains\n')
+ generated_file.write('\tif [[ $narg == 2 ]]; then\n')
+ generated_file.write('\t\topts={}\n'.format(DOMAINS_STR))
+ generated_file.write('\tfi\n\n')
+
+ # If one is currently typing an action then match with the action list
+ # of the previously typed domain
+ generated_file.write('\t# If one already typed a domain,\n')
+ generated_file.write('\t# match the actions of that domain\n')
+ generated_file.write('\tif [[ $narg == 3 ]]; then\n')
+ for domain in DOMAINS:
+ generated_file.write('\t\tif [[ $prev == "{}" ]]; then\n'.format(domain))
+ generated_file.write('\t\t\topts={}\n'.format(ACTIONS_DICT[domain]))
+ generated_file.write('\t\tfi\n')
+ generated_file.write('\tfi\n\n')
+
+ # If both domain and action have been typed or the domain
+ # was not recognized propose --help (only once)
+ generated_file.write('\t# If no options were found propose --help\n')
+ generated_file.write('\tif [ -z "$opts" ]; then\n')
+ generated_file.write('\t\tif [[ $prev != "--help" ]]; then\n')
+ generated_file.write('\t\t\topts=( --help )\n')
+ generated_file.write('\t\tfi\n')
+ generated_file.write('\tfi\n')
+
+ # generate the completion list from the possible options
+ generated_file.write('\tCOMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )\n')
+ generated_file.write('\treturn 0\n')
+ generated_file.write('}\n\n')
+
+ # Add the function to bash completion
+ generated_file.write('complete -F _yunohost yunohost')
diff --git a/data/bash-completion.d/yunohost b/data/bash-completion.d/yunohost
index 106f8fbdf..2572a391d 100644
--- a/data/bash-completion.d/yunohost
+++ b/data/bash-completion.d/yunohost
@@ -1,12 +1,3 @@
-#
-# Bash completion for yunohost
-#
-
-_python_argcomplete() {
- local IFS=''
- COMPREPLY=( $(IFS="$IFS" COMP_LINE="$COMP_LINE" COMP_POINT="$COMP_POINT" _ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS" _ARGCOMPLETE=1 "$1" 8>&1 9>&2 1>/dev/null 2>/dev/null) )
- if [[ $? != 0 ]]; then
- unset COMPREPLY
- fi
-}
-complete -o nospace -o default -F _python_argcomplete "yunohost"
+# This file is automatically generated
+# during Debian's package build by the script
+# data/actionsmap/yunohost_completion.py
diff --git a/data/helpers.d/package b/data/helpers.d/apt
similarity index 61%
rename from data/helpers.d/package
rename to data/helpers.d/apt
index 8b672d701..b4bf60c1f 100644
--- a/data/helpers.d/package
+++ b/data/helpers.d/apt
@@ -1,8 +1,12 @@
+#!/bin/bash
+
# Check if apt is free to use, or wait, until timeout.
#
# [internal]
#
# usage: ynh_wait_dpkg_free
+#
+# Requires YunoHost version 3.3.1 or higher.
ynh_wait_dpkg_free() {
local try
# With seq 1 17, timeout will be almost 30 minutes
@@ -15,6 +19,21 @@ ynh_wait_dpkg_free() {
# Sleep an exponential time at each round
sleep $(( try * try ))
else
+ # Check if dpkg hasn't been interrupted and is fully available.
+ # See this for more information: https://sources.debian.org/src/apt/1.4.9/apt-pkg/deb/debsystem.cc/#L141-L174
+ local dpkg_dir="/var/lib/dpkg/updates/"
+
+ # For each file in $dpkg_dir
+ while read dpkg_file <&9
+ do
+ # Check if the name of this file contains only numbers.
+ if echo "$dpkg_file" | grep -Pq "^[[:digit:]]+$"
+ then
+ # If so, that a remaining of dpkg.
+ ynh_print_err "E: dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem."
+ return 1
+ fi
+ done 9<<< "$(ls -1 $dpkg_dir)"
return 0
fi
done
@@ -23,26 +42,44 @@ ynh_wait_dpkg_free() {
# Check either a package is installed or not
#
-# example: ynh_package_is_installed 'yunohost' && echo "ok"
+# example: ynh_package_is_installed --package=yunohost && echo "ok"
#
-# usage: ynh_package_is_installed name
-# | arg: name - the package name to check
+# usage: ynh_package_is_installed --package=name
+# | arg: -p, --package - the package name to check
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_package_is_installed() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=p
+ declare -Ar args_array=( [p]=package= )
+ local package
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
ynh_wait_dpkg_free
- dpkg-query -W -f '${Status}' "$1" 2>/dev/null \
+ dpkg-query -W -f '${Status}' "$package" 2>/dev/null \
| grep -c "ok installed" &>/dev/null
}
# Get the version of an installed package
#
-# example: version=$(ynh_package_version 'yunohost')
+# example: version=$(ynh_package_version --package=yunohost)
#
-# usage: ynh_package_version name
-# | arg: name - the package name to get version
+# usage: ynh_package_version --package=name
+# | arg: -p, --package - the package name to get version
# | ret: the version or an empty string
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_package_version() {
- if ynh_package_is_installed "$1"; then
- dpkg-query -W -f '${Version}' "$1" 2>/dev/null
+ # Declare an array to define the options of this helper.
+ local legacy_args=p
+ declare -Ar args_array=( [p]=package= )
+ local package
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ if ynh_package_is_installed "$package"; then
+ dpkg-query -W -f '${Version}' "$package" 2>/dev/null
else
echo ''
fi
@@ -53,14 +90,18 @@ ynh_package_version() {
# [internal]
#
# usage: ynh_apt update
+#
+# Requires YunoHost version 2.4.0.3 or higher.
ynh_apt() {
ynh_wait_dpkg_free
- DEBIAN_FRONTEND=noninteractive sudo apt-get -y $@
+ DEBIAN_FRONTEND=noninteractive apt-get -y $@
}
# Update package index files
#
# usage: ynh_package_update
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_package_update() {
ynh_apt update
}
@@ -69,6 +110,8 @@ ynh_package_update() {
#
# usage: ynh_package_install name [name [...]]
# | arg: name - the package name to install
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_package_install() {
ynh_apt --no-remove -o Dpkg::Options::=--force-confdef \
-o Dpkg::Options::=--force-confold install $@
@@ -78,6 +121,8 @@ ynh_package_install() {
#
# usage: ynh_package_remove name [name [...]]
# | arg: name - the package name to remove
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_package_remove() {
ynh_apt remove $@
}
@@ -86,6 +131,8 @@ ynh_package_remove() {
#
# usage: ynh_package_autoremove name [name [...]]
# | arg: name - the package name to remove
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_package_autoremove() {
ynh_apt autoremove $@
}
@@ -94,6 +141,8 @@ ynh_package_autoremove() {
#
# usage: ynh_package_autopurge name [name [...]]
# | arg: name - the package name to autoremove and purge
+#
+# Requires YunoHost version 2.7.2 or higher.
ynh_package_autopurge() {
ynh_apt autoremove --purge $@
}
@@ -108,6 +157,8 @@ ynh_package_autopurge() {
#
# usage: ynh_package_install_from_equivs controlfile
# | arg: controlfile - path of the equivs control file
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_package_install_from_equivs () {
local controlfile=$1
@@ -115,7 +166,7 @@ ynh_package_install_from_equivs () {
local pkgname=$(grep '^Package: ' $controlfile | cut -d' ' -f 2) # Retrieve the name of the debian package
local pkgversion=$(grep '^Version: ' $controlfile | cut -d' ' -f 2) # And its version number
[[ -z "$pkgname" || -z "$pkgversion" ]] \
- && echo "Invalid control file" && exit 1 # Check if this 2 variables aren't empty.
+ && ynh_die --message="Invalid control file" # Check if this 2 variables aren't empty.
# Update packages cache
ynh_package_update
@@ -131,11 +182,11 @@ ynh_package_install_from_equivs () {
# Install the fake package without its dependencies with dpkg
# Install missing dependencies with ynh_package_install
ynh_wait_dpkg_free
- (cp "$controlfile" "${TMPDIR}/control" && cd "$TMPDIR" \
- && equivs-build ./control 1>/dev/null \
- && sudo dpkg --force-depends \
- -i "./${pkgname}_${pkgversion}_all.deb" 2>&1 \
- && ynh_package_install -f) || ynh_die "Unable to install dependencies"
+ cp "$controlfile" "${TMPDIR}/control"
+ (cd "$TMPDIR"
+ equivs-build ./control 1> /dev/null
+ dpkg --force-depends -i "./${pkgname}_${pkgversion}_all.deb" 2>&1)
+ ynh_package_install -f || ynh_die --message="Unable to install dependencies"
[[ -n "$TMPDIR" ]] && rm -rf $TMPDIR # Remove the temp dir.
# check if the package is actually installed
@@ -143,13 +194,15 @@ ynh_package_install_from_equivs () {
}
# Define and install dependencies with a equivs control file
+#
# This helper can/should only be called once per app
#
+# example : ynh_install_app_dependencies dep1 dep2 "dep3|dep4|dep5"
+#
# usage: ynh_install_app_dependencies dep [dep [...]]
-# | arg: dep - the package name to install in dependence
-# You can give a choice between some package with this syntax : "dep1|dep2"
-# Example : ynh_install_app_dependencies dep1 dep2 "dep3|dep4|dep5"
-# This mean in the dependence tree : dep1 & dep2 & (dep3 | dep4 | dep5)
+# | arg: dep - the package name to install in dependence. Writing "dep3|dep4|dep5" can be used to specify alternatives. For example : dep1 dep2 "dep3|dep4|dep5" will require to install dep1 and dep 2 and (dep3 or dep4 or dep5).
+#
+# Requires YunoHost version 2.6.4 or higher.
ynh_install_app_dependencies () {
local dependencies=$@
local dependencies=${dependencies// /, }
@@ -176,9 +229,9 @@ Description: Fake package for ${app} (YunoHost app) dependencies
This meta-package is only responsible of installing its dependencies.
EOF
ynh_package_install_from_equivs /tmp/${dep_app}-ynh-deps.control \
- || ynh_die "Unable to install dependencies" # Install the fake package and its dependencies
+ || ynh_die --message="Unable to install dependencies" # Install the fake package and its dependencies
rm /tmp/${dep_app}-ynh-deps.control
- ynh_app_setting_set $app apt_dependencies $dependencies
+ ynh_app_setting_set --app=$app --key=apt_dependencies --value="$dependencies"
}
# Remove fake package and its dependencies
@@ -186,6 +239,8 @@ EOF
# Dependencies will removed only if no other package need them.
#
# usage: ynh_remove_app_dependencies
+#
+# Requires YunoHost version 2.6.4 or higher.
ynh_remove_app_dependencies () {
local dep_app=${app//_/-} # Replace all '_' by '-'
ynh_package_autopurge ${dep_app}-ynh-deps # Remove the fake package and its dependencies if they not still used.
diff --git a/data/helpers.d/backend b/data/helpers.d/backend
deleted file mode 100644
index 26e53ede9..000000000
--- a/data/helpers.d/backend
+++ /dev/null
@@ -1,244 +0,0 @@
-# Use logrotate to manage the logfile
-#
-# usage: ynh_use_logrotate [logfile] [--non-append|--append] [specific_user/specific_group]
-# | arg: logfile - absolute path of logfile
-# | arg: --non-append - (Option) Replace the config file instead of appending this new config.
-# | arg: specific_user : run logrotate as the specified user and group. If not specified logrotate is runned as root.
-#
-# If no argument provided, a standard directory will be use. /var/log/${app}
-# You can provide a path with the directory only or with the logfile.
-# /parentdir/logdir
-# /parentdir/logdir/logfile.log
-#
-# It's possible to use this helper several times, each config will be added to the same logrotate config file.
-# Unless you use the option --non-append
-ynh_use_logrotate () {
- local customtee="tee -a"
- local user_group="${3:-}"
- if [ $# -gt 0 ] && [ "$1" == "--non-append" ]; then
- customtee="tee"
- # Destroy this argument for the next command.
- shift
- elif [ $# -gt 1 ] && [ "$2" == "--non-append" ]; then
- customtee="tee"
- fi
- if [ $# -gt 0 ]; then
- if [ "$(echo ${1##*.})" == "log" ]; then # Keep only the extension to check if it's a logfile
- local logfile=$1 # In this case, focus logrotate on the logfile
- else
- local logfile=$1/*.log # Else, uses the directory and all logfile into it.
- fi
- else
- local logfile="/var/log/${app}/*.log" # Without argument, use a defaut directory in /var/log
- fi
- local su_directive=""
- if [[ -n $user_group ]]; then
- su_directive=" # Run logorotate as specific user - group
- su ${user_group%/*} ${user_group#*/}"
- fi
-
- cat > ./${app}-logrotate << EOF # Build a config file for logrotate
-$logfile {
- # Rotate if the logfile exceeds 100Mo
- size 100M
- # Keep 12 old log maximum
- rotate 12
- # Compress the logs with gzip
- compress
- # Compress the log at the next cycle. So keep always 2 non compressed logs
- delaycompress
- # Copy and truncate the log to allow to continue write on it. Instead of move the log.
- copytruncate
- # Do not do an error if the log is missing
- missingok
- # Not rotate if the log is empty
- notifempty
- # Keep old logs in the same dir
- noolddir
- $su_directive
-}
-EOF
- sudo mkdir -p $(dirname "$logfile") # Create the log directory, if not exist
- cat ${app}-logrotate | sudo $customtee /etc/logrotate.d/$app > /dev/null # Append this config to the existing config file, or replace the whole config file (depending on $customtee)
-}
-
-# Remove the app's logrotate config.
-#
-# usage: ynh_remove_logrotate
-ynh_remove_logrotate () {
- if [ -e "/etc/logrotate.d/$app" ]; then
- sudo rm "/etc/logrotate.d/$app"
- fi
-}
-
-# Create a dedicated systemd config
-#
-# usage: ynh_add_systemd_config [service] [template]
-# | arg: service - Service name (optionnal, $app by default)
-# | arg: template - Name of template file (optionnal, this is 'systemd' by default, meaning ./conf/systemd.service will be used as template)
-#
-# This will use the template ../conf/.service
-# to generate a systemd config, by replacing the following keywords
-# with global variables that should be defined before calling
-# this helper :
-#
-# __APP__ by $app
-# __FINALPATH__ by $final_path
-#
-ynh_add_systemd_config () {
- local service_name="${1:-$app}"
-
- finalsystemdconf="/etc/systemd/system/$service_name.service"
- ynh_backup_if_checksum_is_different "$finalsystemdconf"
- sudo cp ../conf/${2:-systemd.service} "$finalsystemdconf"
-
- # To avoid a break by set -u, use a void substitution ${var:-}. If the variable is not set, it's simply set with an empty variable.
- # Substitute in a nginx config file only if the variable is not empty
- if test -n "${final_path:-}"; then
- ynh_replace_string "__FINALPATH__" "$final_path" "$finalsystemdconf"
- fi
- if test -n "${app:-}"; then
- ynh_replace_string "__APP__" "$app" "$finalsystemdconf"
- fi
- ynh_store_file_checksum "$finalsystemdconf"
-
- sudo chown root: "$finalsystemdconf"
- sudo systemctl enable $service_name
- sudo systemctl daemon-reload
-}
-
-# Remove the dedicated systemd config
-#
-# usage: ynh_remove_systemd_config [service]
-# | arg: service - Service name (optionnal, $app by default)
-#
-ynh_remove_systemd_config () {
- local service_name="${1:-$app}"
-
- local finalsystemdconf="/etc/systemd/system/$service_name.service"
- if [ -e "$finalsystemdconf" ]; then
- sudo systemctl stop $service_name
- sudo systemctl disable $service_name
- ynh_secure_remove "$finalsystemdconf"
- sudo systemctl daemon-reload
- fi
-}
-
-# Create a dedicated nginx config
-#
-# usage: ynh_add_nginx_config "list of others variables to replace"
-#
-# | arg: list of others variables to replace separeted by a space
-# | for example : 'path_2 port_2 ...'
-#
-# This will use a template in ../conf/nginx.conf
-# __PATH__ by $path_url
-# __DOMAIN__ by $domain
-# __PORT__ by $port
-# __NAME__ by $app
-# __FINALPATH__ by $final_path
-#
-# And dynamic variables (from the last example) :
-# __PATH_2__ by $path_2
-# __PORT_2__ by $port_2
-#
-ynh_add_nginx_config () {
- finalnginxconf="/etc/nginx/conf.d/$domain.d/$app.conf"
- local others_var=${1:-}
- ynh_backup_if_checksum_is_different "$finalnginxconf"
- sudo cp ../conf/nginx.conf "$finalnginxconf"
-
- # To avoid a break by set -u, use a void substitution ${var:-}. If the variable is not set, it's simply set with an empty variable.
- # Substitute in a nginx config file only if the variable is not empty
- if test -n "${path_url:-}"; then
- # path_url_slash_less is path_url, or a blank value if path_url is only '/'
- local path_url_slash_less=${path_url%/}
- ynh_replace_string "__PATH__/" "$path_url_slash_less/" "$finalnginxconf"
- ynh_replace_string "__PATH__" "$path_url" "$finalnginxconf"
- fi
- if test -n "${domain:-}"; then
- ynh_replace_string "__DOMAIN__" "$domain" "$finalnginxconf"
- fi
- if test -n "${port:-}"; then
- ynh_replace_string "__PORT__" "$port" "$finalnginxconf"
- fi
- if test -n "${app:-}"; then
- ynh_replace_string "__NAME__" "$app" "$finalnginxconf"
- fi
- if test -n "${final_path:-}"; then
- ynh_replace_string "__FINALPATH__" "$final_path" "$finalnginxconf"
- fi
-
- # Replace all other variable given as arguments
- for var_to_replace in $others_var
- do
- # ${var_to_replace^^} make the content of the variable on upper-cases
- # ${!var_to_replace} get the content of the variable named $var_to_replace
- ynh_replace_string "__${var_to_replace^^}__" "${!var_to_replace}" "$finalnginxconf"
- done
-
- if [ "${path_url:-}" != "/" ]
- then
- ynh_replace_string "^#sub_path_only" "" "$finalnginxconf"
- else
- ynh_replace_string "^#root_path_only" "" "$finalnginxconf"
- fi
-
- ynh_store_file_checksum "$finalnginxconf"
-
- sudo systemctl reload nginx
-}
-
-# Remove the dedicated nginx config
-#
-# usage: ynh_remove_nginx_config
-ynh_remove_nginx_config () {
- ynh_secure_remove "/etc/nginx/conf.d/$domain.d/$app.conf"
- sudo systemctl reload nginx
-}
-
-# Create a dedicated php-fpm config
-#
-# usage: ynh_add_fpm_config
-ynh_add_fpm_config () {
- # Configure PHP-FPM 7.0 by default
- local fpm_config_dir="/etc/php/7.0/fpm"
- local fpm_service="php7.0-fpm"
- # Configure PHP-FPM 5 on Debian Jessie
- if [ "$(ynh_get_debian_release)" == "jessie" ]; then
- fpm_config_dir="/etc/php5/fpm"
- fpm_service="php5-fpm"
- fi
- ynh_app_setting_set $app fpm_config_dir "$fpm_config_dir"
- ynh_app_setting_set $app fpm_service "$fpm_service"
- finalphpconf="$fpm_config_dir/pool.d/$app.conf"
- ynh_backup_if_checksum_is_different "$finalphpconf"
- sudo cp ../conf/php-fpm.conf "$finalphpconf"
- ynh_replace_string "__NAMETOCHANGE__" "$app" "$finalphpconf"
- ynh_replace_string "__FINALPATH__" "$final_path" "$finalphpconf"
- ynh_replace_string "__USER__" "$app" "$finalphpconf"
- sudo chown root: "$finalphpconf"
- ynh_store_file_checksum "$finalphpconf"
-
- if [ -e "../conf/php-fpm.ini" ]
- then
- echo "Please do not use a separate ini file, merge you directives in the pool file instead." &>2
- fi
- sudo systemctl reload $fpm_service
-}
-
-# Remove the dedicated php-fpm config
-#
-# usage: ynh_remove_fpm_config
-ynh_remove_fpm_config () {
- local fpm_config_dir=$(ynh_app_setting_get $app fpm_config_dir)
- local fpm_service=$(ynh_app_setting_get $app fpm_service)
- # Assume php version 7 if not set
- if [ -z "$fpm_config_dir" ]; then
- fpm_config_dir="/etc/php/7.0/fpm"
- fpm_service="php7.0-fpm"
- fi
- ynh_secure_remove "$fpm_config_dir/pool.d/$app.conf"
- ynh_secure_remove "$fpm_config_dir/conf.d/20-$app.ini" 2>&1
- sudo systemctl reload $fpm_service
-}
diff --git a/data/helpers.d/backup b/data/helpers.d/backup
new file mode 100644
index 000000000..dcf306085
--- /dev/null
+++ b/data/helpers.d/backup
@@ -0,0 +1,452 @@
+#!/bin/bash
+
+CAN_BIND=${CAN_BIND:-1}
+
+# Add a file or a directory to the list of paths to backup
+#
+# This helper can be used both in a system backup hook, and in an app backup script
+#
+# Details: ynh_backup writes SRC and the relative DEST into a CSV file. And it
+# creates the parent destination directory
+#
+# If DEST is ended by a slash it complete this path with the basename of SRC.
+#
+# usage: ynh_backup --src_path=src_path [--dest_path=dest_path] [--is_big] [--not_mandatory]
+# | arg: -s, --src_path - file or directory to bind or symlink or copy. it shouldn't be in the backup dir.
+# | arg: -d, --dest_path - destination file or directory inside the backup dir
+# | arg: -b, --is_big - Indicate data are big (mail, video, image ...)
+# | arg: -m, --not_mandatory - Indicate that if the file is missing, the backup can ignore it.
+# | arg: arg - Deprecated arg
+#
+# Example in the context of a wordpress app
+#
+# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf"
+# # => This line will be added into CSV file
+# # "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/etc/nginx/conf.d/$domain.d/$app.conf"
+#
+# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "conf/nginx.conf"
+# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/nginx.conf"
+#
+# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "conf/"
+# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/$app.conf"
+#
+# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "conf"
+# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf"
+#
+# #Deprecated usages (maintained for retro-compatibility)
+# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "${backup_dir}/conf/nginx.conf"
+# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/nginx.conf"
+#
+# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "/conf/"
+# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/$app.conf"
+#
+# Requires YunoHost version 2.4.0 or higher.
+ynh_backup() {
+ # TODO find a way to avoid injection by file strange naming !
+
+ # Declare an array to define the options of this helper.
+ local legacy_args=sdbm
+ declare -Ar args_array=( [s]=src_path= [d]=dest_path= [b]=is_big [m]=not_mandatory )
+ local src_path
+ local dest_path
+ local is_big
+ local not_mandatory
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ local dest_path="${dest_path:-}"
+ local is_big="${is_big:-0}"
+ local not_mandatory="${not_mandatory:-0}"
+
+ BACKUP_CORE_ONLY=${BACKUP_CORE_ONLY:-0}
+ test -n "${app:-}" && do_not_backup_data=$(ynh_app_setting_get --app=$app --key=do_not_backup_data)
+
+ # If backing up core only (used by ynh_backup_before_upgrade),
+ # don't backup big data items
+ if [ $is_big -eq 1 ] && ( [ ${do_not_backup_data:-0} -eq 1 ] || [ $BACKUP_CORE_ONLY -eq 1 ] )
+ then
+ if [ $BACKUP_CORE_ONLY -eq 1 ]; then
+ ynh_print_warn --message="$src_path will not be saved, because 'BACKUP_CORE_ONLY' is set."
+ else
+ ynh_print_warn --message="$src_path will not be saved, because 'do_not_backup_data' is set."
+ fi
+ return 0
+ fi
+
+ # ==============================================================================
+ # Format correctly source and destination paths
+ # ==============================================================================
+ # Be sure the source path is not empty
+ [[ -e "${src_path}" ]] || {
+ ynh_print_warn --message="Source path '${src_path}' does not exist"
+ if [ "$not_mandatory" == "0" ]
+ then
+ # This is a temporary fix for fail2ban config files missing after the migration to stretch.
+ if echo "${src_path}" | grep --quiet "/etc/fail2ban"
+ then
+ touch "${src_path}"
+ ynh_print_info --message="The missing file will be replaced by a dummy one for the backup !!!"
+ else
+ return 1
+ fi
+ else
+ return 0
+ fi
+ }
+
+ # Transform the source path as an absolute path
+ # If it's a dir remove the ending /
+ src_path=$(realpath "$src_path")
+
+ # If there is no destination path, initialize it with the source path
+ # relative to "/".
+ # eg: src_path=/etc/yunohost -> dest_path=etc/yunohost
+ if [[ -z "$dest_path" ]]; then
+
+ dest_path="${src_path#/}"
+
+ else
+ if [[ "${dest_path:0:1}" == "/" ]]; then
+
+ # If the destination path is an absolute path, transform it as a path
+ # relative to the current working directory ($YNH_CWD)
+ #
+ # If it's an app backup script that run this helper, YNH_CWD is equal to
+ # $YNH_BACKUP_DIR/apps/APP_INSTANCE_NAME/backup/
+ #
+ # If it's a system part backup script, YNH_CWD is equal to $YNH_BACKUP_DIR
+ dest_path="${dest_path#$YNH_CWD/}"
+
+ # Case where $2 is an absolute dir but doesn't begin with $YNH_CWD
+ [[ "${dest_path:0:1}" == "/" ]] \
+ && dest_path="${dest_path#/}"
+ fi
+
+ # Complete dest_path if ended by a /
+ [[ "${dest_path: -1}" == "/" ]] \
+ && dest_path="${dest_path}/$(basename $src_path)"
+ fi
+
+ # Check if dest_path already exists in tmp archive
+ [[ ! -e "${dest_path}" ]] || {
+ ynh_print_err --message="Destination path '${dest_path}' already exist"
+ return 1
+ }
+
+ # Add the relative current working directory to the destination path
+ local rel_dir="${YNH_CWD#$YNH_BACKUP_DIR}"
+ rel_dir="${rel_dir%/}/"
+ dest_path="${rel_dir}${dest_path}"
+ dest_path="${dest_path#/}"
+ # ==============================================================================
+
+ # ==============================================================================
+ # Write file to backup into backup_list
+ # ==============================================================================
+ local src=$(echo "${src_path}" | sed -r 's/"/\"\"/g')
+ local dest=$(echo "${dest_path}" | sed -r 's/"/\"\"/g')
+ echo "\"${src}\",\"${dest}\"" >> "${YNH_BACKUP_CSV}"
+
+ # ==============================================================================
+
+ # Create the parent dir of the destination path
+ # It's for retro compatibility, some script consider ynh_backup creates this dir
+ mkdir -p $(dirname "$YNH_BACKUP_DIR/${dest_path}")
+}
+
+# Restore all files that were previously backuped in a core backup script or app backup script
+#
+# usage: ynh_restore
+#
+# Requires YunoHost version 2.6.4 or higher.
+ynh_restore () {
+ # Deduce the relative path of $YNH_CWD
+ local REL_DIR="${YNH_CWD#$YNH_BACKUP_DIR/}"
+ REL_DIR="${REL_DIR%/}/"
+
+ # For each destination path begining by $REL_DIR
+ cat ${YNH_BACKUP_CSV} | tr -d $'\r' | grep -ohP "^\".*\",\"$REL_DIR.*\"$" | \
+ while read line; do
+ local ORIGIN_PATH=$(echo "$line" | grep -ohP "^\"\K.*(?=\",\".*\"$)")
+ local ARCHIVE_PATH=$(echo "$line" | grep -ohP "^\".*\",\"$REL_DIR\K.*(?=\"$)")
+ ynh_restore_file --origin_path="$ARCHIVE_PATH" --dest_path="$ORIGIN_PATH"
+ done
+}
+
+# Return the path in the archive where has been stocked the origin path
+#
+# [internal]
+#
+# usage: _get_archive_path ORIGIN_PATH
+_get_archive_path () {
+ # For security reasons we use csv python library to read the CSV
+ sudo python -c "
+import sys
+import csv
+with open(sys.argv[1], 'r') as backup_file:
+ backup_csv = csv.DictReader(backup_file, fieldnames=['source', 'dest'])
+ for row in backup_csv:
+ if row['source']==sys.argv[2].strip('\"'):
+ print row['dest']
+ sys.exit(0)
+ raise Exception('Original path for %s not found' % sys.argv[2])
+ " "${YNH_BACKUP_CSV}" "$1"
+ return $?
+}
+
+# Restore a file or a directory
+#
+# Use the registered path in backup_list by ynh_backup to restore the file at
+# the right place.
+#
+# usage: ynh_restore_file --origin_path=origin_path [--dest_path=dest_path] [--not_mandatory]
+# | arg: -o, --origin_path - Path where was located the file or the directory before to be backuped or relative path to $YNH_CWD where it is located in the backup archive
+# | arg: -d, --dest_path - Path where restore the file or the dir, if unspecified, the destination will be ORIGIN_PATH or if the ORIGIN_PATH doesn't exist in the archive, the destination will be searched into backup.csv
+# | arg: -m, --not_mandatory - Indicate that if the file is missing, the restore process can ignore it.
+#
+# examples:
+# ynh_restore_file "/etc/nginx/conf.d/$domain.d/$app.conf"
+# # You can also use relative paths:
+# ynh_restore_file "conf/nginx.conf"
+#
+# If DEST_PATH already exists and is lighter than 500 Mo, a backup will be made in
+# /home/yunohost.conf/backup/. Otherwise, the existing file is removed.
+#
+# if apps/wordpress/etc/nginx/conf.d/$domain.d/$app.conf exists, restore it into
+# /etc/nginx/conf.d/$domain.d/$app.conf
+# if no, search for a match in the csv (eg: conf/nginx.conf) and restore it into
+# /etc/nginx/conf.d/$domain.d/$app.conf
+#
+# Requires YunoHost version 2.6.4 or higher.
+ynh_restore_file () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=odm
+ declare -Ar args_array=( [o]=origin_path= [d]=dest_path= [m]=not_mandatory )
+ local origin_path
+ local archive_path
+ local dest_path
+ local not_mandatory
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ local origin_path="/${origin_path#/}"
+ local archive_path="$YNH_CWD${origin_path}"
+ # Default value for dest_path = /$origin_path
+ local dest_path="${dest_path:-$origin_path}"
+ local not_mandatory="${not_mandatory:-0}"
+
+ # If archive_path doesn't exist, search for a corresponding path in CSV
+ if [ ! -d "$archive_path" ] && [ ! -f "$archive_path" ] && [ ! -L "$archive_path" ]; then
+ if [ "$not_mandatory" == "0" ]
+ then
+ archive_path="$YNH_BACKUP_DIR/$(_get_archive_path \"$origin_path\")"
+ else
+ return 0
+ fi
+ fi
+
+ # Move the old directory if it already exists
+ if [[ -e "${dest_path}" ]]
+ then
+ # Check if the file/dir size is less than 500 Mo
+ if [[ $(du -sb ${dest_path} | cut -d"/" -f1) -le "500000000" ]]
+ then
+ local backup_file="/home/yunohost.conf/backup/${dest_path}.backup.$(date '+%Y%m%d.%H%M%S')"
+ mkdir -p "$(dirname "$backup_file")"
+ mv "${dest_path}" "$backup_file" # Move the current file or directory
+ else
+ ynh_secure_remove --file=${dest_path}
+ fi
+ fi
+
+ # Restore origin_path into dest_path
+ mkdir -p $(dirname "$dest_path")
+
+ # Do a copy if it's just a mounting point
+ if mountpoint -q $YNH_BACKUP_DIR; then
+ if [[ -d "${archive_path}" ]]; then
+ archive_path="${archive_path}/."
+ mkdir -p "$dest_path"
+ fi
+ cp -a "$archive_path" "${dest_path}"
+ # Do a move if YNH_BACKUP_DIR is already a copy
+ else
+ mv "$archive_path" "${dest_path}"
+ fi
+}
+
+# Deprecated helper since it's a dangerous one!
+#
+# [internal]
+#
+ynh_bind_or_cp() {
+ local AS_ROOT=${3:-0}
+ local NO_ROOT=0
+ [[ "${AS_ROOT}" = "1" ]] || NO_ROOT=1
+ ynh_print_warn --message="This helper is deprecated, you should use ynh_backup instead"
+ ynh_backup "$1" "$2" 1
+}
+
+# Calculate and store a file checksum into the app settings
+#
+# $app should be defined when calling this helper
+#
+# usage: ynh_store_file_checksum --file=file
+# | arg: -f, --file - The file on which the checksum will performed, then stored.
+#
+# Requires YunoHost version 2.6.4 or higher.
+ynh_store_file_checksum () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=f
+ declare -Ar args_array=( [f]=file= )
+ local file
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
+ ynh_app_setting_set --app=$app --key=$checksum_setting_name --value=$(sudo md5sum "$file" | cut -d' ' -f1)
+
+ # If backup_file_checksum isn't empty, ynh_backup_if_checksum_is_different has made a backup
+ if [ -n "${backup_file_checksum-}" ]
+ then
+ # Print the diff between the previous file and the new one.
+ # diff return 1 if the files are different, so the || true
+ diff --report-identical-files --unified --color=always $backup_file_checksum $file >&2 || true
+ fi
+ # Unset the variable, so it wouldn't trig a ynh_store_file_checksum without a ynh_backup_if_checksum_is_different before it.
+ unset backup_file_checksum
+}
+
+# Verify the checksum and backup the file if it's different
+#
+# This helper is primarily meant to allow to easily backup personalised/manually
+# modified config files.
+#
+# usage: ynh_backup_if_checksum_is_different --file=file
+# | arg: -f, --file - The file on which the checksum test will be perfomed.
+# | ret: the name of a backup file, or nothing
+#
+# Requires YunoHost version 2.6.4 or higher.
+ynh_backup_if_checksum_is_different () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=f
+ declare -Ar args_array=( [f]=file= )
+ local file
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
+ local checksum_value=$(ynh_app_setting_get --app=$app --key=$checksum_setting_name)
+ # backup_file_checksum isn't declare as local, so it can be reuse by ynh_store_file_checksum
+ backup_file_checksum=""
+ if [ -n "$checksum_value" ]
+ then # Proceed only if a value was stored into the app settings
+ if ! echo "$checksum_value $file" | sudo md5sum -c --status
+ then # If the checksum is now different
+ backup_file_checksum="/home/yunohost.conf/backup/$file.backup.$(date '+%Y%m%d.%H%M%S')"
+ sudo mkdir -p "$(dirname "$backup_file_checksum")"
+ sudo cp -a "$file" "$backup_file_checksum" # Backup the current file
+ ynh_print_warn "File $file has been manually modified since the installation or last upgrade. So it has been duplicated in $backup_file_checksum"
+ echo "$backup_file_checksum" # Return the name of the backup file
+ fi
+ fi
+}
+
+# Delete a file checksum from the app settings
+#
+# $app should be defined when calling this helper
+#
+# usage: ynh_remove_file_checksum file
+# | arg: -f, --file= - The file for which the checksum will be deleted
+#
+# Requires YunoHost version 3.3.1 or higher.
+ynh_delete_file_checksum () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=f
+ declare -Ar args_array=( [f]=file= )
+ local file
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
+ ynh_app_setting_delete --app=$app --key=$checksum_setting_name
+}
+
+# Make a backup in case of failed upgrade
+#
+# usage:
+# ynh_backup_before_upgrade
+# ynh_clean_setup () {
+# ynh_restore_upgradebackup
+# }
+# ynh_abort_if_errors
+#
+# Requires YunoHost version 2.7.2 or higher.
+ynh_backup_before_upgrade () {
+ if [ ! -e "/etc/yunohost/apps/$app/scripts/backup" ]
+ then
+ ynh_print_warn --message="This app doesn't have any backup script."
+ return
+ fi
+ backup_number=1
+ local old_backup_number=2
+ local app_bck=${app//_/-} # Replace all '_' by '-'
+ NO_BACKUP_UPGRADE=${NO_BACKUP_UPGRADE:-0}
+
+ if [ "$NO_BACKUP_UPGRADE" -eq 0 ]
+ then
+ # Check if a backup already exists with the prefix 1
+ if sudo yunohost backup list | grep -q $app_bck-pre-upgrade1
+ then
+ # Prefix becomes 2 to preserve the previous backup
+ backup_number=2
+ old_backup_number=1
+ fi
+
+ # Create backup
+ sudo BACKUP_CORE_ONLY=1 yunohost backup create --apps $app --name $app_bck-pre-upgrade$backup_number --debug
+ if [ "$?" -eq 0 ]
+ then
+ # If the backup succeeded, remove the previous backup
+ if sudo yunohost backup list | grep -q $app_bck-pre-upgrade$old_backup_number
+ then
+ # Remove the previous backup only if it exists
+ sudo yunohost backup delete $app_bck-pre-upgrade$old_backup_number > /dev/null
+ fi
+ else
+ ynh_die --message="Backup failed, the upgrade process was aborted."
+ fi
+ else
+ ynh_print_warn --message="\$NO_BACKUP_UPGRADE is set, backup will be avoided. Be careful, this upgrade is going to be operated without a security backup"
+ fi
+}
+
+# Restore a previous backup if the upgrade process failed
+#
+# usage:
+# ynh_backup_before_upgrade
+# ynh_clean_setup () {
+# ynh_restore_upgradebackup
+# }
+# ynh_abort_if_errors
+#
+# Requires YunoHost version 2.7.2 or higher.
+ynh_restore_upgradebackup () {
+ ynh_print_err --message="Upgrade failed."
+ local app_bck=${app//_/-} # Replace all '_' by '-'
+
+ NO_BACKUP_UPGRADE=${NO_BACKUP_UPGRADE:-0}
+
+ if [ "$NO_BACKUP_UPGRADE" -eq 0 ]
+ then
+ # Check if an existing backup can be found before removing and restoring the application.
+ if sudo yunohost backup list | grep -q $app_bck-pre-upgrade$backup_number
+ then
+ # Remove the application then restore it
+ sudo yunohost app remove $app
+ # Restore the backup
+ sudo yunohost backup restore $app_bck-pre-upgrade$backup_number --apps $app --force --debug
+ ynh_die --message="The app was restored to the way it was before the failed upgrade."
+ fi
+ else
+ ynh_print_warn --message="\$NO_BACKUP_UPGRADE is set, that means there's no backup to restore. You have to fix this upgrade by yourself !"
+ fi
+}
diff --git a/data/helpers.d/fail2ban b/data/helpers.d/fail2ban
new file mode 100644
index 000000000..58af9ec0b
--- /dev/null
+++ b/data/helpers.d/fail2ban
@@ -0,0 +1,151 @@
+#!/bin/bash
+
+# Create a dedicated fail2ban config (jail and filter conf files)
+#
+# usage 1: ynh_add_fail2ban_config --logpath=log_file --failregex=filter [--max_retry=max_retry] [--ports=ports]
+# | arg: -l, --logpath= - Log file to be checked by fail2ban
+# | arg: -r, --failregex= - Failregex to be looked for by fail2ban
+# | arg: -m, --max_retry= - Maximum number of retries allowed before banning IP address - default: 3
+# | arg: -p, --ports= - Ports blocked for a banned IP address - default: http,https
+#
+# -----------------------------------------------------------------------------
+#
+# usage 2: ynh_add_fail2ban_config --use_template [--others_var="list of others variables to replace"]
+# | arg: -t, --use_template - Use this helper in template mode
+# | arg: -v, --others_var= - List of others variables to replace separeted by a space
+# | for example : 'var_1 var_2 ...'
+#
+# This will use a template in ../conf/f2b_jail.conf and ../conf/f2b_filter.conf
+# __APP__ by $app
+#
+# You can dynamically replace others variables by example :
+# __VAR_1__ by $var_1
+# __VAR_2__ by $var_2
+#
+# Generally your template will look like that by example (for synapse):
+#
+# f2b_jail.conf:
+# [__APP__]
+# enabled = true
+# port = http,https
+# filter = __APP__
+# logpath = /var/log/__APP__/logfile.log
+# maxretry = 3
+#
+# f2b_filter.conf:
+# [INCLUDES]
+# before = common.conf
+# [Definition]
+#
+# # Part of regex definition (just used to make more easy to make the global regex)
+# __synapse_start_line = .? \- synapse\..+ \-
+#
+# # Regex definition.
+# failregex = ^%(__synapse_start_line)s INFO \- POST\-(\d+)\- \- \d+ \- Received request\: POST /_matrix/client/r0/login\??%(__synapse_start_line)s INFO \- POST\-\1\- Got login request with identifier: \{u'type': u'm.id.user', u'user'\: u'(.+?)'\}, medium\: None, address: None, user\: u'\5'%(__synapse_start_line)s WARNING \- \- (Attempted to login as @\5\:.+ but they do not exist|Failed password login for user @\5\:.+)$
+#
+# ignoreregex =
+#
+# -----------------------------------------------------------------------------
+#
+# Note about the "failregex" option:
+# regex to match the password failure messages in the logfile. The
+# host must be matched by a group named "host". The tag "" can
+# be used for standard IP/hostname matching and is only an alias for
+# (?:::f{4,6}:)?(?P[\w\-.^_]+)
+#
+# You can find some more explainations about how to make a regex here :
+# https://www.fail2ban.org/wiki/index.php/MANUAL_0_8#Filters
+#
+# Note that the logfile need to exist before to call this helper !!
+#
+# To validate your regex you can test with this command:
+# fail2ban-regex /var/log/YOUR_LOG_FILE_PATH /etc/fail2ban/filter.d/YOUR_APP.conf
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_add_fail2ban_config () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=lrmptv
+ declare -Ar args_array=( [l]=logpath= [r]=failregex= [m]=max_retry= [p]=ports= [t]=use_template [v]=others_var=)
+ local logpath
+ local failregex
+ local max_retry
+ local ports
+ local others_var
+ local use_template
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ use_template="${use_template:-0}"
+ max_retry=${max_retry:-3}
+ ports=${ports:-http,https}
+
+ finalfail2banjailconf="/etc/fail2ban/jail.d/$app.conf"
+ finalfail2banfilterconf="/etc/fail2ban/filter.d/$app.conf"
+ ynh_backup_if_checksum_is_different "$finalfail2banjailconf"
+ ynh_backup_if_checksum_is_different "$finalfail2banfilterconf"
+
+ if [ $use_template -eq 1 ]
+ then
+ # Usage 2, templates
+ cp ../conf/f2b_jail.conf $finalfail2banjailconf
+ cp ../conf/f2b_filter.conf $finalfail2banfilterconf
+
+ if [ -n "${app:-}" ]
+ then
+ ynh_replace_string "__APP__" "$app" "$finalfail2banjailconf"
+ ynh_replace_string "__APP__" "$app" "$finalfail2banfilterconf"
+ fi
+
+ # Replace all other variable given as arguments
+ for var_to_replace in ${others_var:-}; do
+ # ${var_to_replace^^} make the content of the variable on upper-cases
+ # ${!var_to_replace} get the content of the variable named $var_to_replace
+ ynh_replace_string --match_string="__${var_to_replace^^}__" --replace_string="${!var_to_replace}" --target_file="$finalfail2banjailconf"
+ ynh_replace_string --match_string="__${var_to_replace^^}__" --replace_string="${!var_to_replace}" --target_file="$finalfail2banfilterconf"
+ done
+
+ else
+ # Usage 1, no template. Build a config file from scratch.
+ test -n "$logpath" || ynh_die "ynh_add_fail2ban_config expects a logfile path as first argument and received nothing."
+ test -n "$failregex" || ynh_die "ynh_add_fail2ban_config expects a failure regex as second argument and received nothing."
+
+ tee $finalfail2banjailconf < This line will be added into CSV file
-# # "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/etc/nginx/conf.d/$domain.d/$app.conf"
-#
-# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "conf/nginx.conf"
-# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/nginx.conf"
-#
-# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "conf/"
-# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/$app.conf"
-#
-# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "conf"
-# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf"
-#
-# #Deprecated usages (maintained for retro-compatibility)
-# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "${backup_dir}/conf/nginx.conf"
-# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/nginx.conf"
-#
-# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "/conf/"
-# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/$app.conf"
-#
-ynh_backup() {
- # TODO find a way to avoid injection by file strange naming !
- local SRC_PATH="$1"
- local DEST_PATH="${2:-}"
- local IS_BIG="${3:-0}"
- local NOT_MANDATORY="${4:-0}"
- BACKUP_CORE_ONLY=${BACKUP_CORE_ONLY:-0}
-
- # If backing up core only (used by ynh_backup_before_upgrade),
- # don't backup big data items
- if [ "$IS_BIG" == "1" ] && [ "$BACKUP_CORE_ONLY" == "1" ] ; then
- echo "$SRC_PATH will not be saved, because backup_core_only is set." >&2
- return 0
- fi
-
- # ==============================================================================
- # Format correctly source and destination paths
- # ==============================================================================
- # Be sure the source path is not empty
- [[ -e "${SRC_PATH}" ]] || {
- if [ "$NOT_MANDATORY" == "0" ]
- then
- # This is a temporary fix for fail2ban config files missing after the migration to stretch.
- if echo "${SRC_PATH}" | grep --quiet "/etc/fail2ban"
- then
- touch "${SRC_PATH}"
- echo "The missing file will be replaced by a dummy one for the backup !!!" >&2
- else
- return 1
- fi
- else
- return 0
- fi
- }
-
- # Transform the source path as an absolute path
- # If it's a dir remove the ending /
- SRC_PATH=$(realpath "$SRC_PATH")
-
- # If there is no destination path, initialize it with the source path
- # relative to "/".
- # eg: SRC_PATH=/etc/yunohost -> DEST_PATH=etc/yunohost
- if [[ -z "$DEST_PATH" ]]; then
-
- DEST_PATH="${SRC_PATH#/}"
-
- else
- if [[ "${DEST_PATH:0:1}" == "/" ]]; then
-
- # If the destination path is an absolute path, transform it as a path
- # relative to the current working directory ($YNH_CWD)
- #
- # If it's an app backup script that run this helper, YNH_CWD is equal to
- # $YNH_BACKUP_DIR/apps/APP_INSTANCE_NAME/backup/
- #
- # If it's a system part backup script, YNH_CWD is equal to $YNH_BACKUP_DIR
- DEST_PATH="${DEST_PATH#$YNH_CWD/}"
-
- # Case where $2 is an absolute dir but doesn't begin with $YNH_CWD
- [[ "${DEST_PATH:0:1}" == "/" ]] \
- && DEST_PATH="${DEST_PATH#/}"
- fi
-
- # Complete DEST_PATH if ended by a /
- [[ "${DEST_PATH: -1}" == "/" ]] \
- && DEST_PATH="${DEST_PATH}/$(basename $SRC_PATH)"
- fi
-
- # Check if DEST_PATH already exists in tmp archive
- [[ ! -e "${DEST_PATH}" ]] || {
- echo "Destination path '${DEST_PATH}' already exist" >&2
- return 1
- }
-
- # Add the relative current working directory to the destination path
- local REL_DIR="${YNH_CWD#$YNH_BACKUP_DIR}"
- REL_DIR="${REL_DIR%/}/"
- DEST_PATH="${REL_DIR}${DEST_PATH}"
- DEST_PATH="${DEST_PATH#/}"
- # ==============================================================================
-
- # ==============================================================================
- # Write file to backup into backup_list
- # ==============================================================================
- local SRC=$(echo "${SRC_PATH}" | sed -r 's/"/\"\"/g')
- local DEST=$(echo "${DEST_PATH}" | sed -r 's/"/\"\"/g')
- echo "\"${SRC}\",\"${DEST}\"" >> "${YNH_BACKUP_CSV}"
-
- # ==============================================================================
-
- # Create the parent dir of the destination path
- # It's for retro compatibility, some script consider ynh_backup creates this dir
- mkdir -p $(dirname "$YNH_BACKUP_DIR/${DEST_PATH}")
-}
-
-# Restore all files linked to the restore hook or to the restore app script
-#
-# usage: ynh_restore
-#
-ynh_restore () {
- # Deduce the relative path of $YNH_CWD
- local REL_DIR="${YNH_CWD#$YNH_BACKUP_DIR/}"
- REL_DIR="${REL_DIR%/}/"
-
- # For each destination path begining by $REL_DIR
- cat ${YNH_BACKUP_CSV} | tr -d $'\r' | grep -ohP "^\".*\",\"$REL_DIR.*\"$" | \
- while read line; do
- local ORIGIN_PATH=$(echo "$line" | grep -ohP "^\"\K.*(?=\",\".*\"$)")
- local ARCHIVE_PATH=$(echo "$line" | grep -ohP "^\".*\",\"$REL_DIR\K.*(?=\"$)")
- ynh_restore_file "$ARCHIVE_PATH" "$ORIGIN_PATH"
- done
-}
-
-# Return the path in the archive where has been stocked the origin path
-#
-# [internal]
-#
-# usage: _get_archive_path ORIGIN_PATH
-_get_archive_path () {
- # For security reasons we use csv python library to read the CSV
- sudo python -c "
-import sys
-import csv
-with open(sys.argv[1], 'r') as backup_file:
- backup_csv = csv.DictReader(backup_file, fieldnames=['source', 'dest'])
- for row in backup_csv:
- if row['source']==sys.argv[2].strip('\"'):
- print row['dest']
- sys.exit(0)
- raise Exception('Original path for %s not found' % sys.argv[2])
- " "${YNH_BACKUP_CSV}" "$1"
- return $?
-}
-
-# Restore a file or a directory
-#
-# Use the registered path in backup_list by ynh_backup to restore the file at
-# the good place.
-#
-# usage: ynh_restore_file ORIGIN_PATH [ DEST_PATH [NOT_MANDATORY]]
-# | arg: ORIGIN_PATH - Path where was located the file or the directory before
-# to be backuped or relative path to $YNH_CWD where it is located in the backup archive
-# | arg: DEST_PATH - Path where restore the file or the dir, if unspecified,
-# the destination will be ORIGIN_PATH or if the ORIGIN_PATH doesn't exist in
-# the archive, the destination will be searched into backup.csv
-# | arg: NOT_MANDATORY - 1 to indicate that if the file is missing, the restore process can ignore it.
-#
-# If DEST_PATH already exists and is lighter than 500 Mo, a backup will be made in
-# /home/yunohost.conf/backup/. Otherwise, the existing file is removed.
-#
-# examples:
-# ynh_restore_file "/etc/nginx/conf.d/$domain.d/$app.conf"
-# # if apps/wordpress/etc/nginx/conf.d/$domain.d/$app.conf exists, restore it into
-# # /etc/nginx/conf.d/$domain.d/$app.conf
-# # if no, search a correspondance in the csv (eg: conf/nginx.conf) and restore it into
-# # /etc/nginx/conf.d/$domain.d/$app.conf
-#
-# # DON'T GIVE THE ARCHIVE PATH:
-# ynh_restore_file "conf/nginx.conf"
-#
-ynh_restore_file () {
- local ORIGIN_PATH="/${1#/}"
- local ARCHIVE_PATH="$YNH_CWD${ORIGIN_PATH}"
- # Default value for DEST_PATH = /$ORIGIN_PATH
- local DEST_PATH="${2:-$ORIGIN_PATH}"
- local NOT_MANDATORY="${3:-0}"
-
- # If ARCHIVE_PATH doesn't exist, search for a corresponding path in CSV
- if [ ! -d "$ARCHIVE_PATH" ] && [ ! -f "$ARCHIVE_PATH" ] && [ ! -L "$ARCHIVE_PATH" ]; then
- if [ "$NOT_MANDATORY" == "0" ]
- then
- ARCHIVE_PATH="$YNH_BACKUP_DIR/$(_get_archive_path \"$ORIGIN_PATH\")"
- else
- return 0
- fi
- fi
-
- # Move the old directory if it already exists
- if [[ -e "${DEST_PATH}" ]]
- then
- # Check if the file/dir size is less than 500 Mo
- if [[ $(du -sb ${DEST_PATH} | cut -d"/" -f1) -le "500000000" ]]
- then
- local backup_file="/home/yunohost.conf/backup/${DEST_PATH}.backup.$(date '+%Y%m%d.%H%M%S')"
- mkdir -p "$(dirname "$backup_file")"
- mv "${DEST_PATH}" "$backup_file" # Move the current file or directory
- else
- ynh_secure_remove ${DEST_PATH}
- fi
- fi
-
- # Restore ORIGIN_PATH into DEST_PATH
- mkdir -p $(dirname "$DEST_PATH")
-
- # Do a copy if it's just a mounting point
- if mountpoint -q $YNH_BACKUP_DIR; then
- if [[ -d "${ARCHIVE_PATH}" ]]; then
- ARCHIVE_PATH="${ARCHIVE_PATH}/."
- mkdir -p "$DEST_PATH"
- fi
- cp -a "$ARCHIVE_PATH" "${DEST_PATH}"
- # Do a move if YNH_BACKUP_DIR is already a copy
- else
- mv "$ARCHIVE_PATH" "${DEST_PATH}"
- fi
-}
-
-# Deprecated helper since it's a dangerous one!
-#
-# [internal]
-#
-ynh_bind_or_cp() {
- local AS_ROOT=${3:-0}
- local NO_ROOT=0
- [[ "${AS_ROOT}" = "1" ]] || NO_ROOT=1
- echo "This helper is deprecated, you should use ynh_backup instead" >&2
- ynh_backup "$1" "$2" 1
-}
-
-# Create a directory under /tmp
-#
-# [internal]
-#
-# Deprecated helper
-#
-# usage: ynh_mkdir_tmp
-# | ret: the created directory path
-ynh_mkdir_tmp() {
- echo "The helper ynh_mkdir_tmp is deprecated." >&2
- echo "You should use 'mktemp -d' instead and manage permissions \
-properly with chmod/chown." >&2
- local TMP_DIR=$(mktemp -d)
-
- # Give rights to other users could be a security risk.
- # But for retrocompatibility we need it. (This helpers is deprecated)
- chmod 755 $TMP_DIR
- echo $TMP_DIR
-}
-
-# Calculate and store a file checksum into the app settings
-#
-# $app should be defined when calling this helper
-#
-# usage: ynh_store_file_checksum file
-# | arg: file - The file on which the checksum will performed, then stored.
-ynh_store_file_checksum () {
- local checksum_setting_name=checksum_${1//[\/ ]/_} # Replace all '/' and ' ' by '_'
- ynh_app_setting_set $app $checksum_setting_name $(sudo md5sum "$1" | cut -d' ' -f1)
-}
-
-# Verify the checksum and backup the file if it's different
-# This helper is primarily meant to allow to easily backup personalised/manually
-# modified config files.
-#
-# $app should be defined when calling this helper
-#
-# usage: ynh_backup_if_checksum_is_different file
-# | arg: file - The file on which the checksum test will be perfomed.
-#
-# | ret: Return the name a the backup file, or nothing
-ynh_backup_if_checksum_is_different () {
- local file=$1
- local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
- local checksum_value=$(ynh_app_setting_get $app $checksum_setting_name)
- if [ -n "$checksum_value" ]
- then # Proceed only if a value was stored into the app settings
- if ! echo "$checksum_value $file" | sudo md5sum -c --status
- then # If the checksum is now different
- local backup_file="/home/yunohost.conf/backup/$file.backup.$(date '+%Y%m%d.%H%M%S')"
- sudo mkdir -p "$(dirname "$backup_file")"
- sudo cp -a "$file" "$backup_file" # Backup the current file
- echo "File $file has been manually modified since the installation or last upgrade. So it has been duplicated in $backup_file" >&2
- echo "$backup_file" # Return the name of the backup file
- fi
- fi
-}
-
-# Delete a file checksum from the app settings
-#
-# $app should be defined when calling this helper
-#
-# usage: ynh_remove_file_checksum file
-# | arg: -f, --file= - The file for which the checksum will be deleted
-ynh_delete_file_checksum () {
- # Declare an array to define the options of this helper.
- declare -Ar args_array=( [f]=file= )
- local file
- # Manage arguments with getopts
- ynh_handle_getopts_args "$@"
-
- local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
- ynh_app_setting_delete $app $checksum_setting_name
-}
-
-# Remove a file or a directory securely
-#
-# usage: ynh_secure_remove path_to_remove
-# | arg: path_to_remove - File or directory to remove
-ynh_secure_remove () {
- local path_to_remove=$1
- local forbidden_path=" \
- /var/www \
- /home/yunohost.app"
-
- if [[ "$forbidden_path" =~ "$path_to_remove" \
- # Match all paths or subpaths in $forbidden_path
- || "$path_to_remove" =~ ^/[[:alnum:]]+$ \
- # Match all first level paths from / (Like /var, /root, etc...)
- || "${path_to_remove:${#path_to_remove}-1}" = "/" ]]
- # Match if the path finishes by /. Because it seems there is an empty variable
- then
- echo "Avoid deleting $path_to_remove." >&2
- else
- if [ -e "$path_to_remove" ]
- then
- sudo rm -R "$path_to_remove"
- else
- echo "$path_to_remove wasn't deleted because it doesn't exist." >&2
- fi
- fi
-}
diff --git a/data/helpers.d/getopts b/data/helpers.d/getopts
index 6d600e207..c8045fa25 100644
--- a/data/helpers.d/getopts
+++ b/data/helpers.d/getopts
@@ -43,6 +43,8 @@
# To keep a retrocompatibility, a package can still call a helper, using getopts, with positional arguments.
# The "legacy mode" will manage the positional arguments and fill the variable in the same order than they are given in $args_array.
# e.g. for `my_helper "val1" val2`, arg1 will be filled with val1, and arg2 with val2.
+#
+# Requires YunoHost version 3.2.2 or higher.
ynh_handle_getopts_args () {
# Manage arguments only if there's some provided
set +x
@@ -53,33 +55,35 @@ ynh_handle_getopts_args () {
# For each option in the array, reduce to short options for getopts (e.g. for [u]=user, --user will be -u)
# And built parameters string for getopts
- # ${!args_array[@]} is the list of all keys in the array (A key is 'u' in [u]=user, user is a value)
+ # ${!args_array[@]} is the list of all option_flags in the array (An option_flag is 'u' in [u]=user, user is a value)
local getopts_parameters=""
- local key=""
- for key in "${!args_array[@]}"
+ local option_flag=""
+ for option_flag in "${!args_array[@]}"
do
- # Concatenate each keys of the array to build the string of arguments for getopts
+ # Concatenate each option_flags of the array to build the string of arguments for getopts
# Will looks like 'abcd' for -a -b -c -d
- # If the value of a key finish by =, it's an option with additionnal values. (e.g. --user bob or -u bob)
- # Check the last character of the value associate to the key
- if [ "${args_array[$key]: -1}" = "=" ]
+ # If the value of an option_flag finish by =, it's an option with additionnal values. (e.g. --user bob or -u bob)
+ # Check the last character of the value associate to the option_flag
+ if [ "${args_array[$option_flag]: -1}" = "=" ]
then
# For an option with additionnal values, add a ':' after the letter for getopts.
- getopts_parameters="${getopts_parameters}${key}:"
+ getopts_parameters="${getopts_parameters}${option_flag}:"
else
- getopts_parameters="${getopts_parameters}${key}"
+ getopts_parameters="${getopts_parameters}${option_flag}"
fi
# Check each argument given to the function
local arg=""
# ${#arguments[@]} is the size of the array
for arg in `seq 0 $(( ${#arguments[@]} - 1 ))`
do
- # And replace long option (value of the key) by the short option, the key itself
+ # Escape options' values starting with -. Otherwise the - will be considered as another option.
+ arguments[arg]="${arguments[arg]//--${args_array[$option_flag]}-/--${args_array[$option_flag]}\\TOBEREMOVED\\-}"
+ # And replace long option (value of the option_flag) by the short option, the option_flag itself
# (e.g. for [u]=user, --user will be -u)
# Replace long option with =
- arguments[arg]="${arguments[arg]//--${args_array[$key]}/-${key} }"
+ arguments[arg]="${arguments[arg]//--${args_array[$option_flag]}/-${option_flag} }"
# And long option without =
- arguments[arg]="${arguments[arg]//--${args_array[$key]%=}/-${key}}"
+ arguments[arg]="${arguments[arg]//--${args_array[$option_flag]%=}/-${option_flag}}"
done
done
@@ -98,10 +102,10 @@ ynh_handle_getopts_args () {
if [ "$parameter" = "?" ]
then
- ynh_die "Invalid argument: -${OPTARG:-}"
+ ynh_die --message="Invalid argument: -${OPTARG:-}"
elif [ "$parameter" = ":" ]
then
- ynh_die "-$OPTARG parameter requires an argument."
+ ynh_die --message="-$OPTARG parameter requires an argument."
else
local shift_value=1
# Use the long option, corresponding to the short option read by getopts, as a variable
@@ -132,10 +136,11 @@ ynh_handle_getopts_args () {
# Declare the content of option_var as a variable.
eval ${option_var}=""
# Then read the array value per value
+ local i
for i in `seq 0 $(( ${#all_args[@]} - 1 ))`
do
# If this argument is an option, end here.
- if [ "${all_args[$i]:0:1}" == "-" ] || [ -z "${all_args[$i]}" ]
+ if [ "${all_args[$i]:0:1}" == "-" ]
then
# Ignore the first value of the array, which is the option itself
if [ "$i" -ne 0 ]; then
@@ -149,7 +154,18 @@ ynh_handle_getopts_args () {
# If there's already another value for this option, add a ; before adding the new value
eval ${option_var}+="\;"
fi
- eval ${option_var}+=\"${all_args[$i]}\"
+
+ # Remove the \ that escape - at beginning of values.
+ all_args[i]="${all_args[i]//\\TOBEREMOVED\\/}"
+
+ # For the record.
+ # We're using eval here to get the content of the variable stored itself as simple text in $option_var...
+ # Other ways to get that content would be to use either ${!option_var} or declare -g ${option_var}
+ # But... ${!option_var} can't be used as left part of an assignation.
+ # declare -g ${option_var} will create a local variable (despite -g !) and will not be available for the helper itself.
+ # So... Stop fucking arguing each time that eval is evil... Go find an other working solution if you can find one!
+
+ eval ${option_var}+='"${all_args[$i]}"'
shift_value=$(( shift_value + 1 ))
fi
done
@@ -165,25 +181,33 @@ ynh_handle_getopts_args () {
# Check if there's getopts arguments
if [ "${arguments[0]:0:1}" != "-" ]
then
- # If not, enter in legacy mode and manage the arguments as positionnal ones.
- echo "! Helper used in legacy mode !"
+ # If not, enter in legacy mode and manage the arguments as positionnal ones..
+ # Dot not echo, to prevent to go through a helper output. But print only in the log.
+ set -x; echo "! Helper used in legacy mode !" > /dev/null; set +x
+ local i
for i in `seq 0 $(( ${#arguments[@]} -1 ))`
do
- # Use getopts_parameters as a list of key of the array args_array
+ # Try to use legacy_args as a list of option_flag of the array args_array
+ # Otherwise, fallback to getopts_parameters to get the option_flag. But an associative arrays isn't always sorted in the correct order...
# Remove all ':' in getopts_parameters
- getopts_parameters=${getopts_parameters//:}
- # Get the key from getopts_parameters, by using the key according to the position of the argument.
- key=${getopts_parameters:$i:1}
- # Use the long option, corresponding to the key, as a variable
+ getopts_parameters=${legacy_args:-${getopts_parameters//:}}
+ # Get the option_flag from getopts_parameters, by using the option_flag according to the position of the argument.
+ option_flag=${getopts_parameters:$i:1}
+ if [ -z "$option_flag" ]; then
+ ynh_print_warn --message="Too many arguments ! \"${arguments[$i]}\" will be ignored."
+ continue
+ fi
+ # Use the long option, corresponding to the option_flag, as a variable
# (e.g. for [u]=user, 'user' will be used as a variable)
# Also, remove '=' at the end of the long option
# The variable name will be stored in 'option_var'
- local option_var="${args_array[$key]%=}"
+ local option_var="${args_array[$option_flag]%=}"
# Store each value given as argument in the corresponding variable
# The values will be stored in the same order than $args_array
- eval ${option_var}+=\"${arguments[$i]}\"
+ eval ${option_var}+='"${arguments[$i]}"'
done
+ unset legacy_args
else
# END LEGACY MODE
# Call parse_arg and pass the modified list of args as an array of arguments.
diff --git a/data/helpers.d/ip b/data/helpers.d/ip
deleted file mode 100644
index 092cdff4b..000000000
--- a/data/helpers.d/ip
+++ /dev/null
@@ -1,52 +0,0 @@
-# Validate an IP address
-#
-# usage: ynh_validate_ip [family] [ip_address]
-# | ret: 0 for valid ip addresses, 1 otherwise
-#
-# example: ynh_validate_ip 4 111.222.333.444
-#
-ynh_validate_ip()
-{
- # http://stackoverflow.com/questions/319279/how-to-validate-ip-address-in-python#319298
-
- local IP_ADDRESS_FAMILY=$1
- local IP_ADDRESS=$2
-
- [ "$IP_ADDRESS_FAMILY" == "4" ] || [ "$IP_ADDRESS_FAMILY" == "6" ] || return 1
-
- python /dev/stdin << EOF
-import socket
-import sys
-family = { "4" : socket.AF_INET, "6" : socket.AF_INET6 }
-try:
- socket.inet_pton(family["$IP_ADDRESS_FAMILY"], "$IP_ADDRESS")
-except socket.error:
- sys.exit(1)
-sys.exit(0)
-EOF
-}
-
-# Validate an IPv4 address
-#
-# example: ynh_validate_ip4 111.222.333.444
-#
-# usage: ynh_validate_ip4
-# | ret: 0 for valid ipv4 addresses, 1 otherwise
-#
-ynh_validate_ip4()
-{
- ynh_validate_ip 4 $1
-}
-
-
-# Validate an IPv6 address
-#
-# example: ynh_validate_ip6 2000:dead:beef::1
-#
-# usage: ynh_validate_ip6
-# | ret: 0 for valid ipv6 addresses, 1 otherwise
-#
-ynh_validate_ip6()
-{
- ynh_validate_ip 6 $1
-}
diff --git a/data/helpers.d/logging b/data/helpers.d/logging
new file mode 100644
index 000000000..be33b75a5
--- /dev/null
+++ b/data/helpers.d/logging
@@ -0,0 +1,378 @@
+#!/bin/bash
+
+# Print a message to stderr and exit
+#
+# usage: ynh_die --message=MSG [--ret_code=RETCODE]
+#
+# Requires YunoHost version 2.4.0 or higher.
+ynh_die() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=mc
+ declare -Ar args_array=( [m]=message= [c]=ret_code= )
+ local message
+ local ret_code
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ echo "$message" 1>&2
+ exit "${ret_code:-1}"
+}
+
+# Display a message in the 'INFO' logging category
+#
+# usage: ynh_print_info --message="Some message"
+#
+# Requires YunoHost version 3.2.0 or higher.
+ynh_print_info() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=m
+ declare -Ar args_array=( [m]=message= )
+ local message
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ echo "$message" >> "$YNH_STDINFO"
+}
+
+# Ignore the yunohost-cli log to prevent errors with conditional commands
+#
+# [internal]
+#
+# usage: ynh_no_log COMMAND
+#
+# Simply duplicate the log, execute the yunohost command and replace the log without the result of this command
+# It's a very badly hack...
+#
+# Requires YunoHost version 2.6.4 or higher.
+ynh_no_log() {
+ local ynh_cli_log=/var/log/yunohost/yunohost-cli.log
+ sudo cp -a ${ynh_cli_log} ${ynh_cli_log}-move
+ eval $@
+ local exit_code=$?
+ sudo mv ${ynh_cli_log}-move ${ynh_cli_log}
+ return $?
+}
+
+# Main printer, just in case in the future we have to change anything about that.
+#
+# [internal]
+#
+# Requires YunoHost version 3.2.0 or higher.
+ynh_print_log () {
+ echo -e "${1}"
+}
+
+# Print a warning on stderr
+#
+# usage: ynh_print_warn --message="Text to print"
+# | arg: -m, --message - The text to print
+#
+# Requires YunoHost version 3.2.0 or higher.
+ynh_print_warn () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=m
+ declare -Ar args_array=( [m]=message= )
+ local message
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ ynh_print_log "\e[93m\e[1m[WARN]\e[0m ${message}" >&2
+}
+
+# Print an error on stderr
+#
+# usage: ynh_print_err --message="Text to print"
+# | arg: -m, --message - The text to print
+#
+# Requires YunoHost version 3.2.0 or higher.
+ynh_print_err () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=m
+ declare -Ar args_array=( [m]=message= )
+ local message
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ ynh_print_log "\e[91m\e[1m[ERR]\e[0m ${message}" >&2
+}
+
+# Execute a command and print the result as an error
+#
+# usage: ynh_exec_err your_command
+# usage: ynh_exec_err "your_command | other_command"
+#
+# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
+#
+# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
+#
+# | arg: command - command to execute
+#
+# Requires YunoHost version 3.2.0 or higher.
+ynh_exec_err () {
+ ynh_print_err "$(eval $@)"
+}
+
+# Execute a command and print the result as a warning
+#
+# usage: ynh_exec_warn your_command
+# usage: ynh_exec_warn "your_command | other_command"
+#
+# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
+#
+# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
+#
+# | arg: command - command to execute
+#
+# Requires YunoHost version 3.2.0 or higher.
+ynh_exec_warn () {
+ ynh_print_warn "$(eval $@)"
+}
+
+# Execute a command and force the result to be printed on stdout
+#
+# usage: ynh_exec_warn_less your_command
+# usage: ynh_exec_warn_less "your_command | other_command"
+#
+# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
+#
+# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
+#
+# | arg: command - command to execute
+#
+# Requires YunoHost version 3.2.0 or higher.
+ynh_exec_warn_less () {
+ eval $@ 2>&1
+}
+
+# Execute a command and redirect stdout in /dev/null
+#
+# usage: ynh_exec_quiet your_command
+# usage: ynh_exec_quiet "your_command | other_command"
+#
+# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
+#
+# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
+#
+# | arg: command - command to execute
+#
+# Requires YunoHost version 3.2.0 or higher.
+ynh_exec_quiet () {
+ eval $@ > /dev/null
+}
+
+# Execute a command and redirect stdout and stderr in /dev/null
+#
+# usage: ynh_exec_fully_quiet your_command
+# usage: ynh_exec_fully_quiet "your_command | other_command"
+#
+# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
+#
+# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
+#
+# | arg: command - command to execute
+#
+# Requires YunoHost version 3.2.0 or higher.
+ynh_exec_fully_quiet () {
+ eval $@ > /dev/null 2>&1
+}
+
+# Remove any logs for all the following commands.
+#
+# usage: ynh_print_OFF
+#
+# WARNING: You should be careful with this helper, and never forget to use ynh_print_ON as soon as possible to restore the logging.
+#
+# Requires YunoHost version 3.2.0 or higher.
+ynh_print_OFF () {
+ exec {BASH_XTRACEFD}>/dev/null
+}
+
+# Restore the logging after ynh_print_OFF
+#
+# usage: ynh_print_ON
+#
+# Requires YunoHost version 3.2.0 or higher.
+ynh_print_ON () {
+ exec {BASH_XTRACEFD}>&1
+ # Print an echo only for the log, to be able to know that ynh_print_ON has been called.
+ echo ynh_print_ON > /dev/null
+}
+
+# Initial definitions for ynh_script_progression
+increment_progression=0
+previous_weight=0
+max_progression=-1
+# Set the scale of the progression bar
+# progress_string(0,1,2) should have the size of the scale.
+progress_scale=20
+progress_string2="####################"
+progress_string1="++++++++++++++++++++"
+progress_string0="...................."
+# Define base_time when the file is sourced
+base_time=$(date +%s)
+
+# Print a progress bar showing the progression of an app script
+#
+# usage: ynh_script_progression --message=message [--weight=weight] [--time]
+# | arg: -m, --message= - The text to print
+# | arg: -w, --weight= - The weight for this progression. This value is 1 by default. Use a bigger value for a longer part of the script.
+# | arg: -t, --time= - Print the execution time since the last call to this helper. Especially usefull to define weights. The execution time is given for the duration since the previous call. So the weight should be applied to this previous call.
+# | arg: -l, --last= - Use for the last call of the helper, to fill te progression bar.
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_script_progression () {
+ set +x
+ # Declare an array to define the options of this helper.
+ local legacy_args=mwtl
+ declare -Ar args_array=( [m]=message= [w]=weight= [t]=time [l]=last )
+ local message
+ local weight
+ local time
+ local last
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ set +x
+ weight=${weight:-1}
+ time=${time:-0}
+ last=${last:-0}
+
+ # Get execution time since the last $base_time
+ local exec_time=$(( $(date +%s) - $base_time ))
+ base_time=$(date +%s)
+
+ # Compute $max_progression (if we didn't already)
+ if [ "$max_progression" = -1 ]
+ then
+ # Get the number of occurrences of 'ynh_script_progression' in the script. Except those are commented.
+ local helper_calls="$(grep --count "^[^#]*ynh_script_progression" $0)"
+ # Get the number of call with a weight value
+ local weight_calls=$(grep --perl-regexp --count "^[^#]*ynh_script_progression.*(--weight|-w )" $0)
+
+ # Get the weight of each occurrences of 'ynh_script_progression' in the script using --weight
+ local weight_valuesA="$(grep --perl-regexp "^[^#]*ynh_script_progression.*--weight" $0 | sed 's/.*--weight[= ]\([[:digit:]]*\).*/\1/g')"
+ # Get the weight of each occurrences of 'ynh_script_progression' in the script using -w
+ local weight_valuesB="$(grep --perl-regexp "^[^#]*ynh_script_progression.*-w " $0 | sed 's/.*-w[= ]\([[:digit:]]*\).*/\1/g')"
+ # Each value will be on a different line.
+ # Remove each 'end of line' and replace it by a '+' to sum the values.
+ local weight_values=$(( $(echo "$weight_valuesA" | tr '\n' '+') + $(echo "$weight_valuesB" | tr '\n' '+') 0 ))
+
+ # max_progression is a total number of calls to this helper.
+ # Less the number of calls with a weight value.
+ # Plus the total of weight values
+ max_progression=$(( $helper_calls - $weight_calls + $weight_values ))
+ fi
+
+ # Increment each execution of ynh_script_progression in this script by the weight of the previous call.
+ increment_progression=$(( $increment_progression + $previous_weight ))
+ # Store the weight of the current call in $previous_weight for next call
+ previous_weight=$weight
+
+ # Reduce $increment_progression to the size of the scale
+ if [ $last -eq 0 ]
+ then
+ local effective_progression=$(( $increment_progression * $progress_scale / $max_progression ))
+ # If last is specified, fill immediately the progression_bar
+ else
+ local effective_progression=$progress_scale
+ fi
+
+ # Build $progression_bar from progress_string(0,1,2) according to $effective_progression and the weight of the current task
+ # expected_progression is the progression expected after the current task
+ local expected_progression="$(( ( $increment_progression + $weight ) * $progress_scale / $max_progression - $effective_progression ))"
+ if [ $last -eq 1 ]
+ then
+ expected_progression=0
+ fi
+ # left_progression is the progression not yet done
+ local left_progression="$(( $progress_scale - $effective_progression - $expected_progression ))"
+ # Build the progression bar with $effective_progression, work done, $expected_progression, current work and $left_progression, work to be done.
+ local progression_bar="${progress_string2:0:$effective_progression}${progress_string1:0:$expected_progression}${progress_string0:0:$left_progression}"
+
+ local print_exec_time=""
+ if [ $time -eq 1 ]
+ then
+ print_exec_time=" [$(date +%Hh%Mm,%Ss --date="0 + $exec_time sec")]"
+ fi
+
+ ynh_print_info "[$progression_bar] > ${message}${print_exec_time}"
+ set -x
+}
+
+# Return data to the Yunohost core for later processing
+# (to be used by special hooks like app config panel and core diagnosis)
+#
+# usage: ynh_return somedata
+#
+# Requires YunoHost version 3.6.0 or higher.
+ynh_return () {
+ echo "$1" >> "$YNH_STDRETURN"
+}
+
+# Debugger for app packagers
+#
+# usage: ynh_debug [--message=message] [--trace=1/0]
+# | arg: -m, --message= - The text to print
+# | arg: -t, --trace= - Turn on or off the trace of the script. Usefull to trace nonly a small part of a script.
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_debug () {
+ # Disable set xtrace for the helper itself, to not pollute the debug log
+ set +x
+ # Declare an array to define the options of this helper.
+ local legacy_args=mt
+ declare -Ar args_array=( [m]=message= [t]=trace= )
+ local message
+ local trace
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ # Redisable xtrace, ynh_handle_getopts_args set it back
+ set +x
+ message=${message:-}
+ trace=${trace:-}
+
+ if [ -n "$message" ]
+ then
+ ynh_print_log "\e[34m\e[1m[DEBUG]\e[0m ${message}" >&2
+ fi
+
+ if [ "$trace" == "1" ]
+ then
+ ynh_debug --message="Enable debugging"
+ set +x
+ # Get the current file descriptor of xtrace
+ old_bash_xtracefd=$BASH_XTRACEFD
+ # Add the current file name and the line number of any command currently running while tracing.
+ PS4='$(basename ${BASH_SOURCE[0]})-L${LINENO}: '
+ # Force xtrace to stderr
+ BASH_XTRACEFD=2
+ # Force stdout to stderr
+ exec 1>&2
+ fi
+ if [ "$trace" == "0" ]
+ then
+ ynh_debug --message="Disable debugging"
+ set +x
+ # Put xtrace back to its original fild descriptor
+ BASH_XTRACEFD=$old_bash_xtracefd
+ # Restore stdout
+ exec 1>&1
+ fi
+ # Renable set xtrace
+ set -x
+}
+
+# Execute a command and print the result as debug
+#
+# usage: ynh_debug_exec your_command
+# usage: ynh_debug_exec "your_command | other_command"
+#
+# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
+#
+# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
+#
+# | arg: command - command to execute
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_debug_exec () {
+ ynh_debug --message="$(eval $@)"
+}
diff --git a/data/helpers.d/logrotate b/data/helpers.d/logrotate
new file mode 100644
index 000000000..47ce46cf6
--- /dev/null
+++ b/data/helpers.d/logrotate
@@ -0,0 +1,103 @@
+#!/bin/bash
+
+# Use logrotate to manage the logfile
+#
+# usage: ynh_use_logrotate [--logfile=/log/file] [--nonappend] [--specific_user=user/group]
+# | arg: -l, --logfile - absolute path of logfile
+# | arg: -n, --nonappend - (optional) Replace the config file instead of appending this new config.
+# | arg: -u, --specific_user : run logrotate as the specified user and group. If not specified logrotate is runned as root.
+#
+# If no --logfile is provided, /var/log/${app} will be used as default.
+# logfile can be just a directory, or a full path to a logfile :
+# /parentdir/logdir
+# /parentdir/logdir/logfile.log
+#
+# It's possible to use this helper multiple times, each config will be added to
+# the same logrotate config file. Unless you use the option --non-append
+#
+# Requires YunoHost version 2.6.4 or higher.
+ynh_use_logrotate () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=lnuya
+ declare -Ar args_array=( [l]=logfile= [n]=nonappend [u]=specific_user= [y]=non [a]=append )
+ # [y]=non [a]=append are only for legacy purpose, to not fail on the old option '--non-append'
+ local logfile
+ local nonappend
+ local specific_user
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ local logfile="${logfile:-}"
+ local nonappend="${nonappend:-0}"
+ local specific_user="${specific_user:-}"
+
+ # LEGACY CODE - PRE GETOPTS
+ if [ $# -gt 0 ] && [ "$1" == "--non-append" ]; then
+ nonappend=1
+ # Destroy this argument for the next command.
+ shift
+ elif [ $# -gt 1 ] && [ "$2" == "--non-append" ]; then
+ nonappend=1
+ fi
+
+ if [ $# -gt 0 ] && [ "$(echo ${1:0:1})" != "-" ]; then
+ if [ "$(echo ${1##*.})" == "log" ]; then # Keep only the extension to check if it's a logfile
+ local logfile=$1 # In this case, focus logrotate on the logfile
+ else
+ local logfile=$1/*.log # Else, uses the directory and all logfile into it.
+ fi
+ fi
+ # LEGACY CODE
+
+ local customtee="tee -a"
+ if [ "$nonappend" -eq 1 ]; then
+ customtee="tee"
+ fi
+ if [ -n "$logfile" ]
+ then
+ if [ "$(echo ${logfile##*.})" != "log" ]; then # Keep only the extension to check if it's a logfile
+ local logfile="$logfile/*.log" # Else, uses the directory and all logfile into it.
+ fi
+ else
+ logfile="/var/log/${app}/*.log" # Without argument, use a defaut directory in /var/log
+ fi
+ local su_directive=""
+ if [[ -n $specific_user ]]; then
+ su_directive=" # Run logorotate as specific user - group
+ su ${specific_user%/*} ${specific_user#*/}"
+ fi
+
+ cat > ./${app}-logrotate << EOF # Build a config file for logrotate
+$logfile {
+ # Rotate if the logfile exceeds 100Mo
+ size 100M
+ # Keep 12 old log maximum
+ rotate 12
+ # Compress the logs with gzip
+ compress
+ # Compress the log at the next cycle. So keep always 2 non compressed logs
+ delaycompress
+ # Copy and truncate the log to allow to continue write on it. Instead of move the log.
+ copytruncate
+ # Do not do an error if the log is missing
+ missingok
+ # Not rotate if the log is empty
+ notifempty
+ # Keep old logs in the same dir
+ noolddir
+ $su_directive
+}
+EOF
+ sudo mkdir -p $(dirname "$logfile") # Create the log directory, if not exist
+ cat ${app}-logrotate | sudo $customtee /etc/logrotate.d/$app > /dev/null # Append this config to the existing config file, or replace the whole config file (depending on $customtee)
+}
+
+# Remove the app's logrotate config.
+#
+# usage: ynh_remove_logrotate
+#
+# Requires YunoHost version 2.6.4 or higher.
+ynh_remove_logrotate () {
+ if [ -e "/etc/logrotate.d/$app" ]; then
+ sudo rm "/etc/logrotate.d/$app"
+ fi
+}
diff --git a/data/helpers.d/mysql b/data/helpers.d/mysql
index 7bc93fad5..372819025 100644
--- a/data/helpers.d/mysql
+++ b/data/helpers.d/mysql
@@ -1,3 +1,5 @@
+#!/bin/bash
+
MYSQL_ROOT_PWD_FILE=/etc/yunohost/mysql
# Open a connection as a user
@@ -5,32 +7,66 @@ MYSQL_ROOT_PWD_FILE=/etc/yunohost/mysql
# example: ynh_mysql_connect_as 'user' 'pass' <<< "UPDATE ...;"
# example: ynh_mysql_connect_as 'user' 'pass' < /path/to/file.sql
#
-# usage: ynh_mysql_connect_as user pwd [db]
-# | arg: user - the user name to connect as
-# | arg: pwd - the user password
-# | arg: db - the database to connect to
+# usage: ynh_mysql_connect_as --user=user --password=password [--database=database]
+# | arg: -u, --user - the user name to connect as
+# | arg: -p, --password - the user password
+# | arg: -d, --database - the database to connect to
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_mysql_connect_as() {
- mysql -u "$1" --password="$2" -B "${3:-}"
+ # Declare an array to define the options of this helper.
+ local legacy_args=upd
+ declare -Ar args_array=( [u]=user= [p]=password= [d]=database= )
+ local user
+ local password
+ local database
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ database="${database:-}"
+
+ mysql -u "$user" --password="$password" -B "$database"
}
# Execute a command as root user
#
-# usage: ynh_mysql_execute_as_root sql [db]
-# | arg: sql - the SQL command to execute
-# | arg: db - the database to connect to
+# usage: ynh_mysql_execute_as_root --sql=sql [--database=database]
+# | arg: -s, --sql - the SQL command to execute
+# | arg: -d, --database - the database to connect to
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_mysql_execute_as_root() {
- ynh_mysql_connect_as "root" "$(sudo cat $MYSQL_ROOT_PWD_FILE)" \
- "${2:-}" <<< "$1"
+ # Declare an array to define the options of this helper.
+ local legacy_args=sd
+ declare -Ar args_array=( [s]=sql= [d]=database= )
+ local sql
+ local database
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ database="${database:-}"
+
+ ynh_mysql_connect_as --user="root" --password="$(sudo cat $MYSQL_ROOT_PWD_FILE)" \
+ --database="$database" <<< "$sql"
}
# Execute a command from a file as root user
#
-# usage: ynh_mysql_execute_file_as_root file [db]
-# | arg: file - the file containing SQL commands
-# | arg: db - the database to connect to
+# usage: ynh_mysql_execute_file_as_root --file=file [--database=database]
+# | arg: -f, --file - the file containing SQL commands
+# | arg: -d, --database - the database to connect to
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_mysql_execute_file_as_root() {
- ynh_mysql_connect_as "root" "$(sudo cat $MYSQL_ROOT_PWD_FILE)" \
- "${2:-}" < "$1"
+ # Declare an array to define the options of this helper.
+ local legacy_args=fd
+ declare -Ar args_array=( [f]=file= [d]=database= )
+ local file
+ local database
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ database="${database:-}"
+
+ ynh_mysql_connect_as --user="root" --password="$(sudo cat $MYSQL_ROOT_PWD_FILE)" \
+ --database="$database" < "$file"
}
# Create a database and grant optionnaly privilegies to a user
@@ -41,6 +77,8 @@ ynh_mysql_execute_file_as_root() {
# | arg: db - the database name to create
# | arg: user - the user to grant privilegies
# | arg: pwd - the password to identify user by
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_mysql_create_db() {
local db=$1
@@ -53,7 +91,7 @@ ynh_mysql_create_db() {
sql+=" WITH GRANT OPTION;"
fi
- ynh_mysql_execute_as_root "$sql"
+ ynh_mysql_execute_as_root --sql="$sql"
}
# Drop a database
@@ -65,19 +103,30 @@ ynh_mysql_create_db() {
#
# usage: ynh_mysql_drop_db db
# | arg: db - the database name to drop
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_mysql_drop_db() {
- ynh_mysql_execute_as_root "DROP DATABASE ${1};"
+ ynh_mysql_execute_as_root --sql="DROP DATABASE ${1};"
}
# Dump a database
#
# example: ynh_mysql_dump_db 'roundcube' > ./dump.sql
#
-# usage: ynh_mysql_dump_db db
-# | arg: db - the database name to dump
+# usage: ynh_mysql_dump_db --database=database
+# | arg: -d, --database - the database name to dump
# | ret: the mysqldump output
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_mysql_dump_db() {
- mysqldump -u "root" -p"$(sudo cat $MYSQL_ROOT_PWD_FILE)" --single-transaction --skip-dump-date "$1"
+ # Declare an array to define the options of this helper.
+ local legacy_args=d
+ declare -Ar args_array=( [d]=database= )
+ local database
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ mysqldump -u "root" -p"$(sudo cat $MYSQL_ROOT_PWD_FILE)" --single-transaction --skip-dump-date "$database"
}
# Create a user
@@ -87,19 +136,29 @@ ynh_mysql_dump_db() {
# usage: ynh_mysql_create_user user pwd [host]
# | arg: user - the user name to create
# | arg: pwd - the password to identify user by
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_mysql_create_user() {
ynh_mysql_execute_as_root \
- "CREATE USER '${1}'@'localhost' IDENTIFIED BY '${2}';"
+ --sql="CREATE USER '${1}'@'localhost' IDENTIFIED BY '${2}';"
}
# Check if a mysql user exists
#
-# usage: ynh_mysql_user_exists user
-# | arg: user - the user for which to check existence
+# usage: ynh_mysql_user_exists --user=user
+# | arg: -u, --user - the user for which to check existence
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_mysql_user_exists()
{
- local user=$1
- if [[ -z $(ynh_mysql_execute_as_root "SELECT User from mysql.user WHERE User = '$user';") ]]
+ # Declare an array to define the options of this helper.
+ local legacy_args=u
+ declare -Ar args_array=( [u]=user= )
+ local user
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ if [[ -z $(ynh_mysql_execute_as_root --sql="SELECT User from mysql.user WHERE User = '$user';") ]]
then
return 1
else
@@ -113,8 +172,10 @@ ynh_mysql_user_exists()
#
# usage: ynh_mysql_drop_user user
# | arg: user - the user name to drop
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_mysql_drop_user() {
- ynh_mysql_execute_as_root "DROP USER '${1}'@'localhost';"
+ ynh_mysql_execute_as_root --sql="DROP USER '${1}'@'localhost';"
}
# Create a database, an user and its password. Then store the password in the app's config
@@ -122,51 +183,56 @@ ynh_mysql_drop_user() {
# After executing this helper, the password of the created database will be available in $db_pwd
# It will also be stored as "mysqlpwd" into the app settings.
#
-# usage: ynh_mysql_setup_db user name [pwd]
-# | arg: user - Owner of the database
-# | arg: name - Name of the database
-# | arg: pwd - Password of the database. If not given, a password will be generated
+# usage: ynh_mysql_setup_db --db_user=user --db_name=name [--db_pwd=pwd]
+# | arg: -u, --db_user - Owner of the database
+# | arg: -n, --db_name - Name of the database
+# | arg: -p, --db_pwd - Password of the database. If not provided, a password will be generated
+#
+# Requires YunoHost version 2.6.4 or higher.
ynh_mysql_setup_db () {
- local db_user="$1"
- local db_name="$2"
+ # Declare an array to define the options of this helper.
+ local legacy_args=unp
+ declare -Ar args_array=( [u]=db_user= [n]=db_name= [p]=db_pwd= )
+ local db_user
+ local db_name
+ db_pwd=""
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
local new_db_pwd=$(ynh_string_random) # Generate a random password
- # If $3 is not given, use new_db_pwd instead for db_pwd.
- db_pwd="${3:-$new_db_pwd}"
+ # If $db_pwd is not provided, use new_db_pwd instead for db_pwd
+ db_pwd="${db_pwd:-$new_db_pwd}"
+
ynh_mysql_create_db "$db_name" "$db_user" "$db_pwd" # Create the database
- ynh_app_setting_set $app mysqlpwd $db_pwd # Store the password in the app's config
+ ynh_app_setting_set --app=$app --key=mysqlpwd --value=$db_pwd # Store the password in the app's config
}
# Remove a database if it exists, and the associated user
#
-# usage: ynh_mysql_remove_db user name
-# | arg: user - Owner of the database
-# | arg: name - Name of the database
+# usage: ynh_mysql_remove_db --db_user=user --db_name=name
+# | arg: -u, --db_user - Owner of the database
+# | arg: -n, --db_name - Name of the database
+#
+# Requires YunoHost version 2.6.4 or higher.
ynh_mysql_remove_db () {
- local db_user="$1"
- local db_name="$2"
+ # Declare an array to define the options of this helper.
+ local legacy_args=un
+ declare -Ar args_array=( [u]=db_user= [n]=db_name= )
+ local db_user
+ local db_name
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
local mysql_root_password=$(sudo cat $MYSQL_ROOT_PWD_FILE)
if mysqlshow -u root -p$mysql_root_password | grep -q "^| $db_name"; then # Check if the database exists
- echo "Removing database $db_name" >&2
ynh_mysql_drop_db $db_name # Remove the database
else
- echo "Database $db_name not found" >&2
+ ynh_print_warn --message="Database $db_name not found"
fi
# Remove mysql user if it exists
- if $(ynh_mysql_user_exists $db_user); then
+ if $(ynh_mysql_user_exists --user=$db_user); then
ynh_mysql_drop_user $db_user
fi
}
-# Sanitize a string intended to be the name of a database
-# (More specifically : replace - and . by _)
-#
-# example: dbname=$(ynh_sanitize_dbid $app)
-#
-# usage: ynh_sanitize_dbid name
-# | arg: name - name to correct/sanitize
-# | ret: the corrected name
-ynh_sanitize_dbid () {
- local dbid=${1//[-.]/_} # We should avoid having - and . in the name of databases. They are replaced by _
- echo $dbid
-}
diff --git a/data/helpers.d/network b/data/helpers.d/network
index f9e37e6cc..0f75cb165 100644
--- a/data/helpers.d/network
+++ b/data/helpers.d/network
@@ -1,36 +1,22 @@
-# Normalize the url path syntax
-# Handle the slash at the beginning of path and its absence at ending
-# Return a normalized url path
-#
-# example: url_path=$(ynh_normalize_url_path $url_path)
-# ynh_normalize_url_path example -> /example
-# ynh_normalize_url_path /example -> /example
-# ynh_normalize_url_path /example/ -> /example
-# ynh_normalize_url_path / -> /
-#
-# usage: ynh_normalize_url_path path_to_normalize
-# | arg: url_path_to_normalize - URL path to normalize before using it
-ynh_normalize_url_path () {
- local path_url=$1
- test -n "$path_url" || ynh_die "ynh_normalize_url_path expect a URL path as first argument and received nothing."
- if [ "${path_url:0:1}" != "/" ]; then # If the first character is not a /
- path_url="/$path_url" # Add / at begin of path variable
- fi
- if [ "${path_url:${#path_url}-1}" == "/" ] && [ ${#path_url} -gt 1 ]; then # If the last character is a / and that not the only character.
- path_url="${path_url:0:${#path_url}-1}" # Delete the last character
- fi
- echo $path_url
-}
+#!/bin/bash
# Find a free port and return it
#
-# example: port=$(ynh_find_port 8080)
+# example: port=$(ynh_find_port --port=8080)
#
-# usage: ynh_find_port begin_port
-# | arg: begin_port - port to start to search
+# usage: ynh_find_port --port=begin_port
+# | arg: -p, --port - port to start to search
+#
+# Requires YunoHost version 2.6.4 or higher.
ynh_find_port () {
- local port=$1
- test -n "$port" || ynh_die "The argument of ynh_find_port must be a valid port."
+ # Declare an array to define the options of this helper.
+ local legacy_args=p
+ declare -Ar args_array=( [p]=port= )
+ local port
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ test -n "$port" || ynh_die --message="The argument of ynh_find_port must be a valid port."
while netcat -z 127.0.0.1 $port # Check if the port is free
do
port=$((port+1)) # Else, pass to next port
@@ -38,30 +24,77 @@ ynh_find_port () {
echo $port
}
-# Check availability of a web path
+# Validate an IP address
#
-# example: ynh_webpath_available some.domain.tld /coffee
+# usage: ynh_validate_ip --family=family --ip_address=ip_address
+# | ret: 0 for valid ip addresses, 1 otherwise
#
-# usage: ynh_webpath_available domain path
-# | arg: domain - the domain/host of the url
-# | arg: path - the web path to check the availability of
-ynh_webpath_available () {
- local domain=$1
- local path=$2
- sudo yunohost domain url-available $domain $path
+# example: ynh_validate_ip 4 111.222.333.444
+#
+# Requires YunoHost version 2.2.4 or higher.
+ynh_validate_ip()
+{
+ # http://stackoverflow.com/questions/319279/how-to-validate-ip-address-in-python#319298
+
+ # Declare an array to define the options of this helper.
+ local legacy_args=fi
+ declare -Ar args_array=( [f]=family= [i]=ip_address= )
+ local family
+ local ip_address
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ [ "$family" == "4" ] || [ "$family" == "6" ] || return 1
+
+ python /dev/stdin << EOF
+import socket
+import sys
+family = { "4" : socket.AF_INET, "6" : socket.AF_INET6 }
+try:
+ socket.inet_pton(family["$family"], "$ip_address")
+except socket.error:
+ sys.exit(1)
+sys.exit(0)
+EOF
}
-# Register/book a web path for an app
+# Validate an IPv4 address
#
-# example: ynh_webpath_register wordpress some.domain.tld /coffee
+# example: ynh_validate_ip4 111.222.333.444
#
-# usage: ynh_webpath_register app domain path
-# | arg: app - the app for which the domain should be registered
-# | arg: domain - the domain/host of the web path
-# | arg: path - the web path to be registered
-ynh_webpath_register () {
- local app=$1
- local domain=$2
- local path=$3
- sudo yunohost app register-url $app $domain $path
+# usage: ynh_validate_ip4 --ip_address=ip_address
+# | ret: 0 for valid ipv4 addresses, 1 otherwise
+#
+# Requires YunoHost version 2.2.4 or higher.
+ynh_validate_ip4()
+{
+ # Declare an array to define the options of this helper.
+ local legacy_args=i
+ declare -Ar args_array=( [i]=ip_address= )
+ local ip_address
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ ynh_validate_ip 4 $ip_address
+}
+
+
+# Validate an IPv6 address
+#
+# example: ynh_validate_ip6 2000:dead:beef::1
+#
+# usage: ynh_validate_ip6 --ip_address=ip_address
+# | ret: 0 for valid ipv6 addresses, 1 otherwise
+#
+# Requires YunoHost version 2.2.4 or higher.
+ynh_validate_ip6()
+{
+ # Declare an array to define the options of this helper.
+ local legacy_args=i
+ declare -Ar args_array=( [i]=ip_address= )
+ local ip_address
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ ynh_validate_ip 6 $ip_address
}
diff --git a/data/helpers.d/nginx b/data/helpers.d/nginx
new file mode 100644
index 000000000..ce6b61d3c
--- /dev/null
+++ b/data/helpers.d/nginx
@@ -0,0 +1,76 @@
+#!/bin/bash
+
+# Create a dedicated nginx config
+#
+# usage: ynh_add_nginx_config "list of others variables to replace"
+#
+# | arg: list - (Optional) list of others variables to replace separated by spaces. For example : 'path_2 port_2 ...'
+#
+# This will use a template in ../conf/nginx.conf
+# __PATH__ by $path_url
+# __DOMAIN__ by $domain
+# __PORT__ by $port
+# __NAME__ by $app
+# __FINALPATH__ by $final_path
+#
+# And dynamic variables (from the last example) :
+# __PATH_2__ by $path_2
+# __PORT_2__ by $port_2
+#
+# Requires YunoHost version 2.7.2 or higher.
+ynh_add_nginx_config () {
+ finalnginxconf="/etc/nginx/conf.d/$domain.d/$app.conf"
+ local others_var=${1:-}
+ ynh_backup_if_checksum_is_different --file="$finalnginxconf"
+ sudo cp ../conf/nginx.conf "$finalnginxconf"
+
+ # To avoid a break by set -u, use a void substitution ${var:-}. If the variable is not set, it's simply set with an empty variable.
+ # Substitute in a nginx config file only if the variable is not empty
+ if test -n "${path_url:-}"; then
+ # path_url_slash_less is path_url, or a blank value if path_url is only '/'
+ local path_url_slash_less=${path_url%/}
+ ynh_replace_string --match_string="__PATH__/" --replace_string="$path_url_slash_less/" --target_file="$finalnginxconf"
+ ynh_replace_string --match_string="__PATH__" --replace_string="$path_url" --target_file="$finalnginxconf"
+ fi
+ if test -n "${domain:-}"; then
+ ynh_replace_string --match_string="__DOMAIN__" --replace_string="$domain" --target_file="$finalnginxconf"
+ fi
+ if test -n "${port:-}"; then
+ ynh_replace_string --match_string="__PORT__" --replace_string="$port" --target_file="$finalnginxconf"
+ fi
+ if test -n "${app:-}"; then
+ ynh_replace_string --match_string="__NAME__" --replace_string="$app" --target_file="$finalnginxconf"
+ fi
+ if test -n "${final_path:-}"; then
+ ynh_replace_string --match_string="__FINALPATH__" --replace_string="$final_path" --target_file="$finalnginxconf"
+ fi
+
+ # Replace all other variable given as arguments
+ for var_to_replace in $others_var
+ do
+ # ${var_to_replace^^} make the content of the variable on upper-cases
+ # ${!var_to_replace} get the content of the variable named $var_to_replace
+ ynh_replace_string --match_string="__${var_to_replace^^}__" --replace_string="${!var_to_replace}" --target_file="$finalnginxconf"
+ done
+
+ if [ "${path_url:-}" != "/" ]
+ then
+ ynh_replace_string --match_string="^#sub_path_only" --replace_string="" --target_file="$finalnginxconf"
+ else
+ ynh_replace_string --match_string="^#root_path_only" --replace_string="" --target_file="$finalnginxconf"
+ fi
+
+ ynh_store_file_checksum --file="$finalnginxconf"
+
+ ynh_systemd_action --service_name=nginx --action=reload
+}
+
+# Remove the dedicated nginx config
+#
+# usage: ynh_remove_nginx_config
+#
+# Requires YunoHost version 2.7.2 or higher.
+ynh_remove_nginx_config () {
+ ynh_secure_remove --file="/etc/nginx/conf.d/$domain.d/$app.conf"
+ ynh_systemd_action --service_name=nginx --action=reload
+}
diff --git a/data/helpers.d/nodejs b/data/helpers.d/nodejs
index 5111fa671..aabdcb6be 100644
--- a/data/helpers.d/nodejs
+++ b/data/helpers.d/nodejs
@@ -1,3 +1,5 @@
+#!/bin/bash
+
n_install_dir="/opt/node_n"
node_version_path="$n_install_dir/n/versions/node"
# N_PREFIX is the directory of n, it needs to be loaded as a environment variable.
@@ -8,14 +10,16 @@ export N_PREFIX="$n_install_dir"
# [internal]
#
# usage: ynh_install_n
+#
+# Requires YunoHost version 2.7.12 or higher.
ynh_install_n () {
- echo "Installation of N - Node.js version management" >&2
+ ynh_print_info --message="Installation of N - Node.js version management"
# Build an app.src for n
mkdir -p "../conf"
echo "SOURCE_URL=https://github.com/tj/n/archive/v2.1.7.tar.gz
SOURCE_SUM=2ba3c9d4dd3c7e38885b37e02337906a1ee91febe6d5c9159d89a9050f2eea8f" > "../conf/n.src"
# Download and extract n
- ynh_setup_source "$n_install_dir/git" n
+ ynh_setup_source --dest_dir="$n_install_dir/git" --source_id=n
# Install n
(cd "$n_install_dir/git"
PREFIX=$N_PREFIX make install 2>&1)
@@ -34,8 +38,10 @@ SOURCE_SUM=2ba3c9d4dd3c7e38885b37e02337906a1ee91febe6d5c9159d89a9050f2eea8f" > "
# That's means it has to be added to any systemd script.
#
# usage: ynh_use_nodejs
+#
+# Requires YunoHost version 2.7.12 or higher.
ynh_use_nodejs () {
- nodejs_version=$(ynh_app_setting_get $app nodejs_version)
+ nodejs_version=$(ynh_app_setting_get --app=$app --key=nodejs_version)
nodejs_use_version="echo \"Deprecated command, should be removed\""
@@ -53,13 +59,19 @@ ynh_use_nodejs () {
#
# ynh_install_nodejs will install the version of node provided as argument by using n.
#
-# usage: ynh_install_nodejs [nodejs_version]
-# | arg: nodejs_version - Version of node to install.
-# If possible, prefer to use major version number (e.g. 8 instead of 8.10.0).
-# The crontab will handle the update of minor versions when needed.
+# usage: ynh_install_nodejs --nodejs_version=nodejs_version
+# | arg: -n, --nodejs_version - Version of node to install. When possible, your should prefer to use major version number (e.g. 8 instead of 8.10.0). The crontab will then handle the update of minor versions when needed.
+#
+# Requires YunoHost version 2.7.12 or higher.
ynh_install_nodejs () {
# Use n, https://github.com/tj/n to manage the nodejs versions
- nodejs_version="$1"
+
+ # Declare an array to define the options of this helper.
+ local legacy_args=n
+ declare -Ar args_array=( [n]=nodejs_version= )
+ local nodejs_version
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
# Create $n_install_dir
mkdir -p "$n_install_dir"
@@ -80,7 +92,7 @@ ynh_install_nodejs () {
fi
# Modify the default N_PREFIX in n script
- ynh_replace_string "^N_PREFIX=\${N_PREFIX-.*}$" "N_PREFIX=\${N_PREFIX-$N_PREFIX}" "$n_install_dir/bin/n"
+ ynh_replace_string --match_string="^N_PREFIX=\${N_PREFIX-.*}$" --replace_string="N_PREFIX=\${N_PREFIX-$N_PREFIX}" --target_file="$n_install_dir/bin/n"
# Restore /usr/local/bin in PATH
PATH=$CLEAR_PATH
@@ -90,7 +102,13 @@ ynh_install_nodejs () {
test -x /usr/bin/npm_n && mv /usr/bin/npm_n /usr/bin/npm
# Install the requested version of nodejs
- n $nodejs_version
+ uname=$(uname -m)
+ if [[ $uname =~ aarch64 || $uname =~ arm64 ]]
+ then
+ n $nodejs_version --arch=arm64
+ else
+ n $nodejs_version
+ fi
# Find the last "real" version for this major version of node.
real_nodejs_version=$(find $node_version_path/$nodejs_version* -maxdepth 0 | sort --version-sort | tail --lines=1)
@@ -103,10 +121,10 @@ ynh_install_nodejs () {
fi
# Store the ID of this app and the version of node requested for it
- echo "$YNH_APP_ID:$nodejs_version" | tee --append "$n_install_dir/ynh_app_version"
+ echo "$YNH_APP_INSTANCE_NAME:$nodejs_version" | tee --append "$n_install_dir/ynh_app_version"
# Store nodejs_version into the config of this app
- ynh_app_setting_set $app nodejs_version $nodejs_version
+ ynh_app_setting_set --app=$app --key=nodejs_version --value=$nodejs_version
# Build the update script and set the cronjob
ynh_cron_upgrade_node
@@ -121,11 +139,13 @@ ynh_install_nodejs () {
# If no other app uses node, n will be also removed.
#
# usage: ynh_remove_nodejs
+#
+# Requires YunoHost version 2.7.12 or higher.
ynh_remove_nodejs () {
- nodejs_version=$(ynh_app_setting_get $app nodejs_version)
+ nodejs_version=$(ynh_app_setting_get --app=$app --key=nodejs_version)
# Remove the line for this app
- sed --in-place "/$YNH_APP_ID:$nodejs_version/d" "$n_install_dir/ynh_app_version"
+ sed --in-place "/$YNH_APP_INSTANCE_NAME:$nodejs_version/d" "$n_install_dir/ynh_app_version"
# If no other app uses this version of nodejs, remove it.
if ! grep --quiet "$nodejs_version" "$n_install_dir/ynh_app_version"
@@ -136,8 +156,8 @@ ynh_remove_nodejs () {
# If no other app uses n, remove n
if [ ! -s "$n_install_dir/ynh_app_version" ]
then
- ynh_secure_remove "$n_install_dir"
- ynh_secure_remove "/usr/local/n"
+ ynh_secure_remove --file="$n_install_dir"
+ ynh_secure_remove --file="/usr/local/n"
sed --in-place "/N_PREFIX/d" /root/.bashrc
rm -f /etc/cron.daily/node_update
fi
@@ -150,6 +170,8 @@ ynh_remove_nodejs () {
# This cron will check and update all minor node versions used by your apps.
#
# usage: ynh_cron_upgrade_node
+#
+# Requires YunoHost version 2.7.12 or higher.
ynh_cron_upgrade_node () {
# Build the update script
cat > "$n_install_dir/node_update.sh" << EOF
diff --git a/data/helpers.d/php b/data/helpers.d/php
new file mode 100644
index 000000000..c9e3ba9ed
--- /dev/null
+++ b/data/helpers.d/php
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+# Create a dedicated php-fpm config
+#
+# usage: ynh_add_fpm_config [--phpversion=7.X]
+# | arg: -v, --phpversion - Version of php to use.
+#
+# Requires YunoHost version 2.7.2 or higher.
+ynh_add_fpm_config () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=v
+ declare -Ar args_array=( [v]=phpversion= )
+ local phpversion
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ # Configure PHP-FPM 7.0 by default
+ phpversion="${phpversion:-7.0}"
+
+ local fpm_config_dir="/etc/php/$phpversion/fpm"
+ local fpm_service="php${phpversion}-fpm"
+ # Configure PHP-FPM 5 on Debian Jessie
+ if [ "$(ynh_get_debian_release)" == "jessie" ]; then
+ fpm_config_dir="/etc/php5/fpm"
+ fpm_service="php5-fpm"
+ fi
+ ynh_app_setting_set --app=$app --key=fpm_config_dir --value="$fpm_config_dir"
+ ynh_app_setting_set --app=$app --key=fpm_service --value="$fpm_service"
+ finalphpconf="$fpm_config_dir/pool.d/$app.conf"
+ ynh_backup_if_checksum_is_different --file="$finalphpconf"
+ sudo cp ../conf/php-fpm.conf "$finalphpconf"
+ ynh_replace_string --match_string="__NAMETOCHANGE__" --replace_string="$app" --target_file="$finalphpconf"
+ ynh_replace_string --match_string="__FINALPATH__" --replace_string="$final_path" --target_file="$finalphpconf"
+ ynh_replace_string --match_string="__USER__" --replace_string="$app" --target_file="$finalphpconf"
+ ynh_replace_string --match_string="__PHPVERSION__" --replace_string="$phpversion" --target_file="$finalphpconf"
+ sudo chown root: "$finalphpconf"
+ ynh_store_file_checksum --file="$finalphpconf"
+
+ if [ -e "../conf/php-fpm.ini" ]
+ then
+ echo "Packagers ! Please do not use a separate php ini file, merge your directives in the pool file instead." >&2
+ finalphpini="$fpm_config_dir/conf.d/20-$app.ini"
+ ynh_backup_if_checksum_is_different "$finalphpini"
+ sudo cp ../conf/php-fpm.ini "$finalphpini"
+ sudo chown root: "$finalphpini"
+ ynh_store_file_checksum "$finalphpini"
+ fi
+ ynh_systemd_action --service_name=$fpm_service --action=reload
+}
+
+# Remove the dedicated php-fpm config
+#
+# usage: ynh_remove_fpm_config
+#
+# Requires YunoHost version 2.7.2 or higher.
+ynh_remove_fpm_config () {
+ local fpm_config_dir=$(ynh_app_setting_get --app=$app --key=fpm_config_dir)
+ local fpm_service=$(ynh_app_setting_get --app=$app --key=fpm_service)
+ # Assume php version 7 if not set
+ if [ -z "$fpm_config_dir" ]; then
+ fpm_config_dir="/etc/php/7.0/fpm"
+ fpm_service="php7.0-fpm"
+ fi
+ ynh_secure_remove --file="$fpm_config_dir/pool.d/$app.conf"
+ ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini" 2>&1
+ ynh_systemd_action --service_name=$fpm_service --action=reload
+}
diff --git a/data/helpers.d/postgresql b/data/helpers.d/postgresql
new file mode 100644
index 000000000..a76580b11
--- /dev/null
+++ b/data/helpers.d/postgresql
@@ -0,0 +1,298 @@
+#!/bin/bash
+
+PSQL_ROOT_PWD_FILE=/etc/yunohost/psql
+
+# Open a connection as a user
+#
+# examples:
+# ynh_psql_connect_as 'user' 'pass' <<< "UPDATE ...;"
+# ynh_psql_connect_as 'user' 'pass' < /path/to/file.sql
+#
+# usage: ynh_psql_connect_as --user=user --password=password [--database=database]
+# | arg: -u, --user - the user name to connect as
+# | arg: -p, --password - the user password
+# | arg: -d, --database - the database to connect to
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_psql_connect_as() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=upd
+ declare -Ar args_array=([u]=user= [p]=password= [d]=database=)
+ local user
+ local password
+ local database
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ database="${database:-}"
+
+ sudo --login --user=postgres PGUSER="$user" PGPASSWORD="$password" psql "$database"
+}
+
+# Execute a command as root user
+#
+# usage: ynh_psql_execute_as_root --sql=sql [--database=database]
+# | arg: -s, --sql - the SQL command to execute
+# | arg: -d, --database - the database to connect to
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_psql_execute_as_root() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=sd
+ declare -Ar args_array=([s]=sql= [d]=database=)
+ local sql
+ local database
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ database="${database:-}"
+
+ ynh_psql_connect_as --user="postgres" --password="$(sudo cat $PSQL_ROOT_PWD_FILE)" \
+ --database="$database" <<<"$sql"
+}
+
+# Execute a command from a file as root user
+#
+# usage: ynh_psql_execute_file_as_root --file=file [--database=database]
+# | arg: -f, --file - the file containing SQL commands
+# | arg: -d, --database - the database to connect to
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_psql_execute_file_as_root() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=fd
+ declare -Ar args_array=([f]=file= [d]=database=)
+ local file
+ local database
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ database="${database:-}"
+
+ ynh_psql_connect_as --user="postgres" --password="$(sudo cat $PSQL_ROOT_PWD_FILE)" \
+ --database="$database" <"$file"
+}
+
+# Create a database and grant optionnaly privilegies to a user
+#
+# [internal]
+#
+# usage: ynh_psql_create_db db [user]
+# | arg: db - the database name to create
+# | arg: user - the user to grant privilegies
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_psql_create_db() {
+ local db=$1
+ local user=${2:-}
+
+ local sql="CREATE DATABASE ${db};"
+
+ # grant all privilegies to user
+ if [ -n "$user" ]; then
+ sql+="GRANT ALL PRIVILEGES ON DATABASE ${db} TO ${user} WITH GRANT OPTION;"
+ fi
+
+ ynh_psql_execute_as_root --sql="$sql"
+}
+
+# Drop a database
+#
+# [internal]
+#
+# If you intend to drop the database *and* the associated user,
+# consider using ynh_psql_remove_db instead.
+#
+# usage: ynh_psql_drop_db db
+# | arg: db - the database name to drop
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_psql_drop_db() {
+ local db=$1
+ # First, force disconnection of all clients connected to the database
+ # https://stackoverflow.com/questions/5408156/how-to-drop-a-postgresql-database-if-there-are-active-connections-to-it
+ # https://dba.stackexchange.com/questions/16426/how-to-drop-all-connections-to-a-specific-database-without-stopping-the-server
+ ynh_psql_execute_as_root --sql="SELECT pg_terminate_backend (pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '$db';" --database="$db"
+ sudo --login --user=postgres dropdb $db
+}
+
+# Dump a database
+#
+# example: ynh_psql_dump_db 'roundcube' > ./dump.sql
+#
+# usage: ynh_psql_dump_db --database=database
+# | arg: -d, --database - the database name to dump
+# | ret: the psqldump output
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_psql_dump_db() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=d
+ declare -Ar args_array=([d]=database=)
+ local database
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ sudo --login --user=postgres pg_dump "$database"
+}
+
+# Create a user
+#
+# [internal]
+#
+# usage: ynh_psql_create_user user pwd
+# | arg: user - the user name to create
+# | arg: pwd - the password to identify user by
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_psql_create_user() {
+ local user=$1
+ local pwd=$2
+ ynh_psql_execute_as_root --sql="CREATE USER $user WITH ENCRYPTED PASSWORD '$pwd'"
+}
+
+# Check if a psql user exists
+#
+# usage: ynh_psql_user_exists --user=user
+# | arg: -u, --user - the user for which to check existence
+ynh_psql_user_exists() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=u
+ declare -Ar args_array=([u]=user=)
+ local user
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ if ! sudo --login --user=postgres PGUSER="postgres" PGPASSWORD="$(sudo cat $PSQL_ROOT_PWD_FILE)" psql -tAc "SELECT rolname FROM pg_roles WHERE rolname='$user';" | grep --quiet "$user" ; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+# Check if a psql database exists
+#
+# usage: ynh_psql_database_exists --database=database
+# | arg: -d, --database - the database for which to check existence
+ynh_psql_database_exists() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=d
+ declare -Ar args_array=([d]=database=)
+ local database
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ if ! sudo --login --user=postgres PGUSER="postgres" PGPASSWORD="$(sudo cat $PSQL_ROOT_PWD_FILE)" psql -tAc "SELECT datname FROM pg_database WHERE datname='$database';" | grep --quiet "$database"; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+# Drop a user
+#
+# [internal]
+#
+# usage: ynh_psql_drop_user user
+# | arg: user - the user name to drop
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_psql_drop_user() {
+ ynh_psql_execute_as_root --sql="DROP USER ${1};"
+}
+
+# Create a database, an user and its password. Then store the password in the app's config
+#
+# After executing this helper, the password of the created database will be available in $db_pwd
+# It will also be stored as "psqlpwd" into the app settings.
+#
+# usage: ynh_psql_setup_db --db_user=user --db_name=name [--db_pwd=pwd]
+# | arg: -u, --db_user - Owner of the database
+# | arg: -n, --db_name - Name of the database
+# | arg: -p, --db_pwd - Password of the database. If not given, a password will be generated
+ynh_psql_setup_db() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=unp
+ declare -Ar args_array=([u]=db_user= [n]=db_name= [p]=db_pwd=)
+ local db_user
+ local db_name
+ db_pwd=""
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ local new_db_pwd=$(ynh_string_random) # Generate a random password
+ # If $db_pwd is not given, use new_db_pwd instead for db_pwd
+ db_pwd="${db_pwd:-$new_db_pwd}"
+
+ if ! ynh_psql_user_exists --user=$db_user; then
+ ynh_psql_create_user "$db_user" "$db_pwd"
+ fi
+
+ ynh_psql_create_db "$db_name" "$db_user" # Create the database
+ ynh_app_setting_set --app=$app --key=psqlpwd --value=$db_pwd # Store the password in the app's config
+}
+
+# Remove a database if it exists, and the associated user
+#
+# usage: ynh_psql_remove_db --db_user=user --db_name=name
+# | arg: -u, --db_user - Owner of the database
+# | arg: -n, --db_name - Name of the database
+ynh_psql_remove_db() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=un
+ declare -Ar args_array=([u]=db_user= [n]=db_name=)
+ local db_user
+ local db_name
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ local psql_root_password=$(sudo cat $PSQL_ROOT_PWD_FILE)
+ if ynh_psql_database_exists --database=$db_name; then # Check if the database exists
+ ynh_psql_drop_db $db_name # Remove the database
+ else
+ ynh_print_warn --message="Database $db_name not found"
+ fi
+
+ # Remove psql user if it exists
+ if ynh_psql_user_exists --user=$db_user; then
+ ynh_psql_drop_user $db_user
+ else
+ ynh_print_warn --message="User $db_user not found"
+ fi
+}
+
+# Create a master password and set up global settings
+# Please always call this script in install and restore scripts
+#
+# usage: ynh_psql_test_if_first_run
+ynh_psql_test_if_first_run() {
+ if [ -f "$PSQL_ROOT_PWD_FILE" ]; then
+ echo "PostgreSQL is already installed, no need to create master password"
+ else
+ local psql_root_password="$(ynh_string_random)"
+ echo "$psql_root_password" >$PSQL_ROOT_PWD_FILE
+
+ if [ -e /etc/postgresql/9.4/ ]; then
+ local pg_hba=/etc/postgresql/9.4/main/pg_hba.conf
+ local logfile=/var/log/postgresql/postgresql-9.4-main.log
+ elif [ -e /etc/postgresql/9.6/ ]; then
+ local pg_hba=/etc/postgresql/9.6/main/pg_hba.conf
+ local logfile=/var/log/postgresql/postgresql-9.6-main.log
+ else
+ ynh_die "postgresql shoud be 9.4 or 9.6"
+ fi
+
+ ynh_systemd_action --service_name=postgresql --action=start
+
+ sudo --login --user=postgres psql -c"ALTER user postgres WITH PASSWORD '$psql_root_password'" postgres
+
+ # force all user to connect to local database using passwords
+ # https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html#EXAMPLE-PG-HBA.CONF
+ # Note: we can't use peer since YunoHost create users with nologin
+ # See: https://github.com/YunoHost/yunohost/blob/unstable/data/helpers.d/user
+ ynh_replace_string --match_string="local\(\s*\)all\(\s*\)all\(\s*\)peer" --replace_string="local\1all\2all\3password" --target_file="$pg_hba"
+
+ # Advertise service in admin panel
+ yunohost service add postgresql --log "$logfile"
+
+ systemctl enable postgresql
+ ynh_systemd_action --service_name=postgresql --action=reload
+ fi
+}
diff --git a/data/helpers.d/print b/data/helpers.d/print
deleted file mode 100644
index 2f451bc24..000000000
--- a/data/helpers.d/print
+++ /dev/null
@@ -1,126 +0,0 @@
-# Print a message to stderr and exit
-# usage: ynh_die MSG [RETCODE]
-ynh_die() {
- echo "$1" 1>&2
- exit "${2:-1}"
-}
-
-# Display a message in the 'INFO' logging category
-#
-# usage: ynh_print_info "Some message"
-ynh_print_info() {
- echo "$1" >> "$YNH_STDINFO"
-}
-
-# Ignore the yunohost-cli log to prevent errors with conditional commands
-#
-# [internal]
-#
-# usage: ynh_no_log COMMAND
-#
-# Simply duplicate the log, execute the yunohost command and replace the log without the result of this command
-# It's a very badly hack...
-ynh_no_log() {
- local ynh_cli_log=/var/log/yunohost/yunohost-cli.log
- sudo cp -a ${ynh_cli_log} ${ynh_cli_log}-move
- eval $@
- local exit_code=$?
- sudo mv ${ynh_cli_log}-move ${ynh_cli_log}
- return $?
-}
-
-# Main printer, just in case in the future we have to change anything about that.
-#
-# [internal]
-#
-ynh_print_log () {
- echo -e "${1}"
-}
-
-# Print a warning on stderr
-#
-# usage: ynh_print_warn "Text to print"
-# | arg: text - The text to print
-ynh_print_warn () {
- ynh_print_log "\e[93m\e[1m[WARN]\e[0m ${1}" >&2
-}
-
-# Print an error on stderr
-#
-# usage: ynh_print_err "Text to print"
-# | arg: text - The text to print
-ynh_print_err () {
- ynh_print_log "\e[91m\e[1m[ERR]\e[0m ${1}" >&2
-}
-
-# Execute a command and print the result as an error
-#
-# usage: ynh_exec_err command to execute
-# usage: ynh_exec_err "command to execute | following command"
-# In case of use of pipes, you have to use double quotes. Otherwise, this helper will be executed with the first command, then be sent to the next pipe.
-#
-# | arg: command - command to execute
-ynh_exec_err () {
- ynh_print_err "$(eval $@)"
-}
-
-# Execute a command and print the result as a warning
-#
-# usage: ynh_exec_warn command to execute
-# usage: ynh_exec_warn "command to execute | following command"
-# In case of use of pipes, you have to use double quotes. Otherwise, this helper will be executed with the first command, then be sent to the next pipe.
-#
-# | arg: command - command to execute
-ynh_exec_warn () {
- ynh_print_warn "$(eval $@)"
-}
-
-# Execute a command and force the result to be printed on stdout
-#
-# usage: ynh_exec_warn_less command to execute
-# usage: ynh_exec_warn_less "command to execute | following command"
-# In case of use of pipes, you have to use double quotes. Otherwise, this helper will be executed with the first command, then be sent to the next pipe.
-#
-# | arg: command - command to execute
-ynh_exec_warn_less () {
- eval $@ 2>&1
-}
-
-# Execute a command and redirect stdout in /dev/null
-#
-# usage: ynh_exec_quiet command to execute
-# usage: ynh_exec_quiet "command to execute | following command"
-# In case of use of pipes, you have to use double quotes. Otherwise, this helper will be executed with the first command, then be sent to the next pipe.
-#
-# | arg: command - command to execute
-ynh_exec_quiet () {
- eval $@ > /dev/null
-}
-
-# Execute a command and redirect stdout and stderr in /dev/null
-#
-# usage: ynh_exec_fully_quiet command to execute
-# usage: ynh_exec_fully_quiet "command to execute | following command"
-# In case of use of pipes, you have to use double quotes. Otherwise, this helper will be executed with the first command, then be sent to the next pipe.
-#
-# | arg: command - command to execute
-ynh_exec_fully_quiet () {
- eval $@ > /dev/null 2>&1
-}
-
-# Remove any logs for all the following commands.
-#
-# usage: ynh_print_OFF
-# WARNING: You should be careful with this helper, and never forget to use ynh_print_ON as soon as possible to restore the logging.
-ynh_print_OFF () {
- set +x
-}
-
-# Restore the logging after ynh_print_OFF
-#
-# usage: ynh_print_ON
-ynh_print_ON () {
- set -x
- # Print an echo only for the log, to be able to know that ynh_print_ON has been called.
- echo ynh_print_ON > /dev/null
-}
diff --git a/data/helpers.d/psql b/data/helpers.d/psql
deleted file mode 100644
index 2ef13482a..000000000
--- a/data/helpers.d/psql
+++ /dev/null
@@ -1,148 +0,0 @@
-# Create a master password and set up global settings
-# Please always call this script in install and restore scripts
-#
-# usage: ynh_psql_test_if_first_run
-ynh_psql_test_if_first_run() {
- if [ -f /etc/yunohost/psql ];
- then
- echo "PostgreSQL is already installed, no need to create master password"
- else
- local pgsql="$(ynh_string_random)"
- echo "$pgsql" > /etc/yunohost/psql
-
- if [ -e /etc/postgresql/9.4/ ]
- then
- local pg_hba=/etc/postgresql/9.4/main/pg_hba.conf
- elif [ -e /etc/postgresql/9.6/ ]
- then
- local pg_hba=/etc/postgresql/9.6/main/pg_hba.conf
- else
- ynh_die "postgresql shoud be 9.4 or 9.6"
- fi
-
- systemctl start postgresql
- sudo --login --user=postgres psql -c"ALTER user postgres WITH PASSWORD '$pgsql'" postgres
-
- # force all user to connect to local database using passwords
- # https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html#EXAMPLE-PG-HBA.CONF
- # Note: we can't use peer since YunoHost create users with nologin
- # See: https://github.com/YunoHost/yunohost/blob/unstable/data/helpers.d/user
- sed -i '/local\s*all\s*all\s*peer/i \
- local all all password' "$pg_hba"
- systemctl enable postgresql
- systemctl reload postgresql
- fi
-}
-
-# Open a connection as a user
-#
-# example: ynh_psql_connect_as 'user' 'pass' <<< "UPDATE ...;"
-# example: ynh_psql_connect_as 'user' 'pass' < /path/to/file.sql
-#
-# usage: ynh_psql_connect_as user pwd [db]
-# | arg: user - the user name to connect as
-# | arg: pwd - the user password
-# | arg: db - the database to connect to
-ynh_psql_connect_as() {
- local user="$1"
- local pwd="$2"
- local db="$3"
- sudo --login --user=postgres PGUSER="$user" PGPASSWORD="$pwd" psql "$db"
-}
-
-# # Execute a command as root user
-#
-# usage: ynh_psql_execute_as_root sql [db]
-# | arg: sql - the SQL command to execute
-ynh_psql_execute_as_root () {
- local sql="$1"
- sudo --login --user=postgres psql <<< "$sql"
-}
-
-# Execute a command from a file as root user
-#
-# usage: ynh_psql_execute_file_as_root file [db]
-# | arg: file - the file containing SQL commands
-# | arg: db - the database to connect to
-ynh_psql_execute_file_as_root() {
- local file="$1"
- local db="$2"
- sudo --login --user=postgres psql "$db" < "$file"
-}
-
-# Create a database, an user and its password. Then store the password in the app's config
-#
-# After executing this helper, the password of the created database will be available in $db_pwd
-# It will also be stored as "psqlpwd" into the app settings.
-#
-# usage: ynh_psql_setup_db user name [pwd]
-# | arg: user - Owner of the database
-# | arg: name - Name of the database
-# | arg: pwd - Password of the database. If not given, a password will be generated
-ynh_psql_setup_db () {
- local db_user="$1"
- local db_name="$2"
- local new_db_pwd=$(ynh_string_random) # Generate a random password
- # If $3 is not given, use new_db_pwd instead for db_pwd.
- local db_pwd="${3:-$new_db_pwd}"
- ynh_psql_create_db "$db_name" "$db_user" "$db_pwd" # Create the database
- ynh_app_setting_set "$app" psqlpwd "$db_pwd" # Store the password in the app's config
-}
-
-# Create a database and grant privilegies to a user
-#
-# usage: ynh_psql_create_db db [user [pwd]]
-# | arg: db - the database name to create
-# | arg: user - the user to grant privilegies
-# | arg: pwd - the user password
-ynh_psql_create_db() {
- local db="$1"
- local user="$2"
- local pwd="$3"
- ynh_psql_create_user "$user" "$pwd"
- sudo --login --user=postgres createdb --owner="$user" "$db"
-}
-
-# Drop a database
-#
-# usage: ynh_psql_drop_db db
-# | arg: db - the database name to drop
-# | arg: user - the user to drop
-ynh_psql_remove_db() {
- local db="$1"
- local user="$2"
- sudo --login --user=postgres dropdb "$db"
- ynh_psql_drop_user "$user"
-}
-
-# Dump a database
-#
-# example: ynh_psql_dump_db 'roundcube' > ./dump.sql
-#
-# usage: ynh_psql_dump_db db
-# | arg: db - the database name to dump
-# | ret: the psqldump output
-ynh_psql_dump_db() {
- local db="$1"
- sudo --login --user=postgres pg_dump "$db"
-}
-
-
-# Create a user
-#
-# usage: ynh_psql_create_user user pwd [host]
-# | arg: user - the user name to create
-ynh_psql_create_user() {
- local user="$1"
- local pwd="$2"
- sudo --login --user=postgres psql -c"CREATE USER $user WITH PASSWORD '$pwd'" postgres
-}
-
-# Drop a user
-#
-# usage: ynh_psql_drop_user user
-# | arg: user - the user name to drop
-ynh_psql_drop_user() {
- local user="$1"
- sudo --login --user=postgres dropuser "$user"
-}
diff --git a/data/helpers.d/setting b/data/helpers.d/setting
index ad036ba4f..da711b4bd 100644
--- a/data/helpers.d/setting
+++ b/data/helpers.d/setting
@@ -1,27 +1,299 @@
+#!/bin/bash
+
# Get an application setting
#
-# usage: ynh_app_setting_get app key
-# | arg: app - the application id
-# | arg: key - the setting to get
+# usage: ynh_app_setting_get --app=app --key=key
+# | arg: -a, --app - the application id
+# | arg: -k, --key - the setting to get
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_app_setting_get() {
- sudo yunohost app setting "$1" "$2" --output-as plain --quiet
+ # Declare an array to define the options of this helper.
+ local legacy_args=ak
+ declare -Ar args_array=( [a]=app= [k]=key= )
+ local app
+ local key
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ ynh_app_setting "get" "$app" "$key"
}
# Set an application setting
#
-# usage: ynh_app_setting_set app key value
-# | arg: app - the application id
-# | arg: key - the setting name to set
-# | arg: value - the setting value to set
+# usage: ynh_app_setting_set --app=app --key=key --value=value
+# | arg: -a, --app - the application id
+# | arg: -k, --key - the setting name to set
+# | arg: -v, --value - the setting value to set
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_app_setting_set() {
- sudo yunohost app setting "$1" "$2" --value="$3" --quiet
+ # Declare an array to define the options of this helper.
+ local legacy_args=akv
+ declare -Ar args_array=( [a]=app= [k]=key= [v]=value= )
+ local app
+ local key
+ local value
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ ynh_app_setting "set" "$app" "$key" "$value"
}
# Delete an application setting
#
-# usage: ynh_app_setting_delete app key
-# | arg: app - the application id
-# | arg: key - the setting to delete
+# usage: ynh_app_setting_delete --app=app --key=key
+# | arg: -a, --app - the application id
+# | arg: -k, --key - the setting to delete
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_app_setting_delete() {
- sudo yunohost app setting -d "$1" "$2" --quiet
+ # Declare an array to define the options of this helper.
+ local legacy_args=ak
+ declare -Ar args_array=( [a]=app= [k]=key= )
+ local app
+ local key
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ ynh_app_setting "delete" "$app" "$key"
+}
+
+# Add skipped_uris urls into the config
+#
+# usage: ynh_add_skipped_uris [--appid=app] --url=url1,url2 [--regex]
+# | arg: -a, --appid - the application id
+# | arg: -u, --url - the urls to add to the sso for this app
+# | arg: -r, --regex - Use the key 'skipped_regex' instead of 'skipped_uris'
+#
+# An URL set with 'skipped_uris' key will be totally ignored by the SSO,
+# which means that the access will be public and the logged-in user information will not be passed to the app.
+#
+# Requires YunoHost version 3.6.0 or higher.
+ynh_add_skipped_uris() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=aur
+ declare -Ar args_array=( [a]=appid= [u]=url= [r]=regex )
+ local appid
+ local url
+ local regex
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ appid={appid:-$app}
+ regex={regex:-0}
+
+ local key=skipped_uris
+ if [ $regex -eq 1 ]; then
+ key=skipped_regex
+ fi
+
+ ynh_app_setting_set --app=$appid --key=$key --value="$url"
+}
+
+# Add unprotected_uris urls into the config
+#
+# usage: ynh_add_unprotected_uris [--appid=app] --url=url1,url2 [--regex]
+# | arg: -a, --appid - the application id
+# | arg: -u, --url - the urls to add to the sso for this app
+# | arg: -r, --regex - Use the key 'unprotected_regex' instead of 'unprotected_uris'
+#
+# An URL set with unprotected_uris key will be accessible publicly, but if an user is logged in,
+# his information will be accessible (through HTTP headers) to the app.
+#
+# Requires YunoHost version 3.6.0 or higher.
+ynh_add_unprotected_uris() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=aur
+ declare -Ar args_array=( [a]=appid= [u]=url= [r]=regex )
+ local appid
+ local url
+ local regex
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ appid={appid:-$app}
+ regex={regex:-0}
+
+ local key=unprotected_uris
+ if [ $regex -eq 1 ]; then
+ key=unprotected_regex
+ fi
+
+ ynh_app_setting_set --app=$appid --key=$key --value="$url"
+}
+
+# Add protected_uris urls into the config
+#
+# usage: ynh_add_protected_uris [--appid=app] --url=url1,url2 [--regex]
+# | arg: -a, --appid - the application id
+# | arg: -u, --url - the urls to add to the sso for this app
+# | arg: -r, --regex - Use the key 'protected_regex' instead of 'protected_uris'
+#
+# An URL set with protected_uris will be blocked by the SSO and accessible only to authenticated and authorized users.
+#
+# Requires YunoHost version 3.6.0 or higher.
+ynh_add_protected_uris() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=aur
+ declare -Ar args_array=( [a]=appid= [u]=url= [r]=regex )
+ local appid
+ local url
+ local regex
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ appid={appid:-$app}
+ regex={regex:-0}
+
+ local key=protected_uris
+ if [ $regex -eq 1 ]; then
+ key=protected_regex
+ fi
+
+ ynh_app_setting_set --app=$appid --key=$key --value="$url"
+}
+
+# Small "hard-coded" interface to avoid calling "yunohost app" directly each
+# time dealing with a setting is needed (which may be so slow on ARM boards)
+#
+# [internal]
+#
+ynh_app_setting()
+{
+ ACTION="$1" APP="$2" KEY="$3" VALUE="${4:-}" python - < /dev/null \
| tr -c -d 'A-Za-z0-9' \
- | sed -n 's/\(.\{'"${1:-24}"'\}\).*/\1/p'
+ | sed -n 's/\(.\{'"$length"'\}\).*/\1/p'
}
# Substitute/replace a string (or expression) by another in a file
#
-# usage: ynh_replace_string match_string replace_string target_file
-# | arg: match_string - String to be searched and replaced in the file
-# | arg: replace_string - String that will replace matches
-# | arg: target_file - File in which the string will be replaced.
+# usage: ynh_replace_string --match_string=match_string --replace_string=replace_string --target_file=target_file
+# | arg: -m, --match_string - String to be searched and replaced in the file
+# | arg: -r, --replace_string - String that will replace matches
+# | arg: -f, --target_file - File in which the string will be replaced.
#
# As this helper is based on sed command, regular expressions and
# references to sub-expressions can be used
# (see sed manual page for more information)
+#
+# Requires YunoHost version 2.6.4 or higher.
ynh_replace_string () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=mrf
+ declare -Ar args_array=( [m]=match_string= [r]=replace_string= [f]=target_file= )
+ local match_string
+ local replace_string
+ local target_file
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
local delimit=@
- local match_string=$1
- local replace_string=$2
- local workfile=$3
-
# Escape the delimiter if it's in the string.
match_string=${match_string//${delimit}/"\\${delimit}"}
replace_string=${replace_string//${delimit}/"\\${delimit}"}
- sudo sed --in-place "s${delimit}${match_string}${delimit}${replace_string}${delimit}g" "$workfile"
+ sudo sed --in-place "s${delimit}${match_string}${delimit}${replace_string}${delimit}g" "$target_file"
}
# Substitute/replace a special string by another in a file
#
-# usage: ynh_replace_special_string match_string replace_string target_file
-# | arg: match_string - String to be searched and replaced in the file
-# | arg: replace_string - String that will replace matches
-# | arg: target_file - File in which the string will be replaced.
+# usage: ynh_replace_special_string --match_string=match_string --replace_string=replace_string --target_file=target_file
+# | arg: -m, --match_string - String to be searched and replaced in the file
+# | arg: -r, --replace_string - String that will replace matches
+# | arg: -t, --target_file - File in which the string will be replaced.
#
# This helper will use ynh_replace_string, but as you can use special
# characters, you can't use some regular expressions and sub-expressions.
+#
+# Requires YunoHost version 2.7.7 or higher.
ynh_replace_special_string () {
- local match_string=$1
- local replace_string=$2
- local workfile=$3
+ # Declare an array to define the options of this helper.
+ local legacy_args=mrf
+ declare -Ar args_array=( [m]=match_string= [r]=replace_string= [f]=target_file= )
+ local match_string
+ local replace_string
+ local target_file
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
- # Escape any backslash to preserve them as simple backslash.
- match_string=${match_string//\\/"\\\\"}
- replace_string=${replace_string//\\/"\\\\"}
+ # Escape any backslash to preserve them as simple backslash.
+ match_string=${match_string//\\/"\\\\"}
+ replace_string=${replace_string//\\/"\\\\"}
# Escape the & character, who has a special function in sed.
match_string=${match_string//&/"\&"}
replace_string=${replace_string//&/"\&"}
- ynh_replace_string "$match_string" "$replace_string" "$workfile"
+ ynh_replace_string --match_string="$match_string" --replace_string="$replace_string" --target_file="$target_file"
+}
+
+# Sanitize a string intended to be the name of a database
+# (More specifically : replace - and . by _)
+#
+# example: dbname=$(ynh_sanitize_dbid $app)
+#
+# usage: ynh_sanitize_dbid --db_name=name
+# | arg: -n, --db_name - name to correct/sanitize
+# | ret: the corrected name
+#
+# Requires YunoHost version 2.2.4 or higher.
+ynh_sanitize_dbid () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=n
+ declare -Ar args_array=( [n]=db_name= )
+ local db_name
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ # We should avoid having - and . in the name of databases. They are replaced by _
+ echo ${db_name//[-.]/_}
+}
+
+# Normalize the url path syntax
+#
+# Handle the slash at the beginning of path and its absence at ending
+# Return a normalized url path
+#
+# examples:
+# url_path=$(ynh_normalize_url_path $url_path)
+# ynh_normalize_url_path example # -> /example
+# ynh_normalize_url_path /example # -> /example
+# ynh_normalize_url_path /example/ # -> /example
+# ynh_normalize_url_path / # -> /
+#
+# usage: ynh_normalize_url_path --path_url=path_to_normalize
+# | arg: -p, --path_url - URL path to normalize before using it
+#
+# Requires YunoHost version 2.6.4 or higher.
+ynh_normalize_url_path () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=p
+ declare -Ar args_array=( [p]=path_url= )
+ local path_url
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ test -n "$path_url" || ynh_die --message="ynh_normalize_url_path expect a URL path as first argument and received nothing."
+ if [ "${path_url:0:1}" != "/" ]; then # If the first character is not a /
+ path_url="/$path_url" # Add / at begin of path variable
+ fi
+ if [ "${path_url:${#path_url}-1}" == "/" ] && [ ${#path_url} -gt 1 ]; then # If the last character is a / and that not the only character.
+ path_url="${path_url:0:${#path_url}-1}" # Delete the last character
+ fi
+ echo $path_url
}
diff --git a/data/helpers.d/system b/data/helpers.d/system
deleted file mode 100644
index 70cc57493..000000000
--- a/data/helpers.d/system
+++ /dev/null
@@ -1,55 +0,0 @@
-# Manage a fail of the script
-#
-# [internal]
-#
-# usage:
-# ynh_exit_properly is used only by the helper ynh_abort_if_errors.
-# You should not use it directly.
-# Instead, add to your script:
-# ynh_clean_setup () {
-# instructions...
-# }
-#
-# This function provide a way to clean some residual of installation that not managed by remove script.
-#
-# It prints a warning to inform that the script was failed, and execute the ynh_clean_setup function if used in the app script
-#
-ynh_exit_properly () {
- local exit_code=$?
- if [ "$exit_code" -eq 0 ]; then
- exit 0 # Exit without error if the script ended correctly
- fi
-
- trap '' EXIT # Ignore new exit signals
- set +eu # Do not exit anymore if a command fail or if a variable is empty
-
- echo -e "!!\n $app's script has encountered an error. Its execution was cancelled.\n!!" >&2
-
- if type -t ynh_clean_setup > /dev/null; then # Check if the function exist in the app script.
- ynh_clean_setup # Call the function to do specific cleaning for the app.
- fi
-
- ynh_die # Exit with error status
-}
-
-# Exits if an error occurs during the execution of the script.
-#
-# usage: ynh_abort_if_errors
-#
-# This configure the rest of the script execution such that, if an error occurs
-# or if an empty variable is used, the execution of the script stops
-# immediately and a call to `ynh_clean_setup` is triggered if it has been
-# defined by your script.
-#
-ynh_abort_if_errors () {
- set -eu # Exit if a command fail, and if a variable is used unset.
- trap ynh_exit_properly EXIT # Capturing exit signals on shell script
-}
-
-# Fetch the Debian release codename
-#
-# usage: ynh_get_debian_release
-# | ret: The Debian release codename (i.e. jessie, stretch, ...)
-ynh_get_debian_release () {
- echo $(lsb_release --codename --short)
-}
diff --git a/data/helpers.d/systemd b/data/helpers.d/systemd
new file mode 100644
index 000000000..4b3b5a289
--- /dev/null
+++ b/data/helpers.d/systemd
@@ -0,0 +1,173 @@
+#!/bin/bash
+
+# Create a dedicated systemd config
+#
+# usage: ynh_add_systemd_config [--service=service] [--template=template]
+# | arg: -s, --service - Service name (optionnal, $app by default)
+# | arg: -t, --template - Name of template file (optionnal, this is 'systemd' by default, meaning ./conf/systemd.service will be used as template)
+#
+# This will use the template ../conf/.service
+# to generate a systemd config, by replacing the following keywords
+# with global variables that should be defined before calling
+# this helper :
+#
+# __APP__ by $app
+# __FINALPATH__ by $final_path
+#
+# Requires YunoHost version 2.7.2 or higher.
+ynh_add_systemd_config () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=st
+ declare -Ar args_array=( [s]=service= [t]=template= )
+ local service
+ local template
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ local service="${service:-$app}"
+ local template="${template:-systemd.service}"
+
+ finalsystemdconf="/etc/systemd/system/$service.service"
+ ynh_backup_if_checksum_is_different --file="$finalsystemdconf"
+ sudo cp ../conf/$template "$finalsystemdconf"
+
+ # To avoid a break by set -u, use a void substitution ${var:-}. If the variable is not set, it's simply set with an empty variable.
+ # Substitute in a nginx config file only if the variable is not empty
+ if test -n "${final_path:-}"; then
+ ynh_replace_string --match_string="__FINALPATH__" --replace_string="$final_path" --target_file="$finalsystemdconf"
+ fi
+ if test -n "${app:-}"; then
+ ynh_replace_string --match_string="__APP__" --replace_string="$app" --target_file="$finalsystemdconf"
+ fi
+ ynh_store_file_checksum --file="$finalsystemdconf"
+
+ sudo chown root: "$finalsystemdconf"
+ sudo systemctl enable $service
+ sudo systemctl daemon-reload
+}
+
+# Remove the dedicated systemd config
+#
+# usage: ynh_remove_systemd_config [--service=service]
+# | arg: -s, --service - Service name (optionnal, $app by default)
+#
+# Requires YunoHost version 2.7.2 or higher.
+ynh_remove_systemd_config () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=s
+ declare -Ar args_array=( [s]=service= )
+ local service
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ local service="${service:-$app}"
+
+ local finalsystemdconf="/etc/systemd/system/$service.service"
+ if [ -e "$finalsystemdconf" ]; then
+ ynh_systemd_action --service_name=$service --action=stop
+ systemctl disable $service
+ ynh_secure_remove --file="$finalsystemdconf"
+ systemctl daemon-reload
+ fi
+}
+
+# Start (or other actions) a service, print a log in case of failure and optionnaly wait until the service is completely started
+#
+# usage: ynh_systemd_action [-n service_name] [-a action] [ [-l "line to match"] [-p log_path] [-t timeout] [-e length] ]
+# | arg: -n, --service_name= - Name of the service to start. Default : $app
+# | arg: -a, --action= - Action to perform with systemctl. Default: start
+# | arg: -l, --line_match= - Line to match - The line to find in the log to attest the service have finished to boot. If not defined it don't wait until the service is completely started. WARNING: When using --line_match, you should always add `ynh_clean_check_starting` into your `ynh_clean_setup` at the beginning of the script. Otherwise, tail will not stop in case of failure of the script. The script will then hang forever.
+# | arg: -p, --log_path= - Log file - Path to the log file. Default : /var/log/$app/$app.log
+# | arg: -t, --timeout= - Timeout - The maximum time to wait before ending the watching. Default : 300 seconds.
+# | arg: -e, --length= - Length of the error log : Default : 20
+ynh_systemd_action() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=nalpte
+ declare -Ar args_array=( [n]=service_name= [a]=action= [l]=line_match= [p]=log_path= [t]=timeout= [e]=length= )
+ local service_name
+ local action
+ local line_match
+ local length
+ local log_path
+ local timeout
+
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ local service_name="${service_name:-$app}"
+ local action=${action:-start}
+ local log_path="${log_path:-/var/log/$service_name/$service_name.log}"
+ local length=${length:-20}
+ local timeout=${timeout:-300}
+
+ # Start to read the log
+ if [[ -n "${line_match:-}" ]]
+ then
+ local templog="$(mktemp)"
+ # Following the starting of the app in its log
+ if [ "$log_path" == "systemd" ] ; then
+ # Read the systemd journal
+ journalctl --unit=$service_name --follow --since=-0 --quiet > "$templog" &
+ # Get the PID of the journalctl command
+ local pid_tail=$!
+ else
+ # Read the specified log file
+ tail -F -n0 "$log_path" > "$templog" 2>&1 &
+ # Get the PID of the tail command
+ local pid_tail=$!
+ fi
+ fi
+
+ # Use reload-or-restart instead of reload. So it wouldn't fail if the service isn't running.
+ if [ "$action" == "reload" ]; then
+ action="reload-or-restart"
+ fi
+
+ systemctl $action $service_name \
+ || ( journalctl --no-pager --lines=$length -u $service_name >&2 \
+ ; test -e "$log_path" && echo "--" >&2 && tail --lines=$length "$log_path" >&2 \
+ ; false )
+
+ # Start the timeout and try to find line_match
+ if [[ -n "${line_match:-}" ]]
+ then
+ local i=0
+ for i in $(seq 1 $timeout)
+ do
+ # Read the log until the sentence is found, that means the app finished to start. Or run until the timeout
+ if grep --quiet "$line_match" "$templog"
+ then
+ ynh_print_info --message="The service $service_name has correctly started."
+ break
+ fi
+ if [ $i -eq 3 ]; then
+ echo -n "Please wait, the service $service_name is ${action}ing" >&2
+ fi
+ if [ $i -ge 3 ]; then
+ echo -n "." >&2
+ fi
+ sleep 1
+ done
+ if [ $i -ge 3 ]; then
+ echo "" >&2
+ fi
+ if [ $i -eq $timeout ]
+ then
+ ynh_print_warn --message="The service $service_name didn't fully started before the timeout."
+ ynh_print_warn --message="Please find here an extract of the end of the log of the service $service_name:"
+ journalctl --no-pager --lines=$length -u $service_name >&2
+ test -e "$log_path" && echo "--" >&2 && tail --lines=$length "$log_path" >&2
+ fi
+ ynh_clean_check_starting
+ fi
+}
+
+# Clean temporary process and file used by ynh_check_starting
+# (usually used in ynh_clean_setup scripts)
+#
+# usage: ynh_clean_check_starting
+ynh_clean_check_starting () {
+ # Stop the execution of tail.
+ kill -s 15 $pid_tail 2>&1
+ ynh_secure_remove "$templog" 2>&1
+}
+
+
diff --git a/data/helpers.d/user b/data/helpers.d/user
index 47e6eb88a..e7890ccb2 100644
--- a/data/helpers.d/user
+++ b/data/helpers.d/user
@@ -1,23 +1,44 @@
+#!/bin/bash
+
# Check if a YunoHost user exists
#
# example: ynh_user_exists 'toto' || exit 1
#
-# usage: ynh_user_exists username
-# | arg: username - the username to check
+# usage: ynh_user_exists --username=username
+# | arg: -u, --username - the username to check
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_user_exists() {
- sudo yunohost user list --output-as json | grep -q "\"username\": \"${1}\""
+ # Declare an array to define the options of this helper.
+ local legacy_args=u
+ declare -Ar args_array=( [u]=username= )
+ local username
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ sudo yunohost user list --output-as json | grep -q "\"username\": \"${username}\""
}
# Retrieve a YunoHost user information
#
# example: mail=$(ynh_user_get_info 'toto' 'mail')
#
-# usage: ynh_user_get_info username key
-# | arg: username - the username to retrieve info from
-# | arg: key - the key to retrieve
+# usage: ynh_user_get_info --username=username --key=key
+# | arg: -u, --username - the username to retrieve info from
+# | arg: -k, --key - the key to retrieve
# | ret: string - the key's value
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_user_get_info() {
- sudo yunohost user info "$1" --output-as plain | ynh_get_plain_key "$2"
+ # Declare an array to define the options of this helper.
+ local legacy_args=uk
+ declare -Ar args_array=( [u]=username= [k]=key= )
+ local username
+ local key
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ sudo yunohost user info "$username" --output-as plain | ynh_get_plain_key "$key"
}
# Get the list of YunoHost users
@@ -26,6 +47,8 @@ ynh_user_get_info() {
#
# usage: ynh_user_list
# | ret: string - one username per line
+#
+# Requires YunoHost version 2.4.0 or higher.
ynh_user_list() {
sudo yunohost user list --output-as plain --quiet \
| awk '/^##username$/{getline; print}'
@@ -33,39 +56,103 @@ ynh_user_list() {
# Check if a user exists on the system
#
-# usage: ynh_system_user_exists username
-# | arg: username - the username to check
+# usage: ynh_system_user_exists --username=username
+# | arg: -u, --username - the username to check
+#
+# Requires YunoHost version 2.2.4 or higher.
ynh_system_user_exists() {
- getent passwd "$1" &>/dev/null
+ # Declare an array to define the options of this helper.
+ local legacy_args=u
+ declare -Ar args_array=( [u]=username= )
+ local username
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ getent passwd "$username" &>/dev/null
+}
+
+# Check if a group exists on the system
+#
+# usage: ynh_system_group_exists --group=group
+# | arg: -g, --group - the group to check
+ynh_system_group_exists() {
+ # Declare an array to define the options of this helper.
+ local legacy_args=g
+ declare -Ar args_array=( [g]=group= )
+ local group
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ getent group "$group" &>/dev/null
}
# Create a system user
#
-# usage: ynh_system_user_create user_name [home_dir]
-# | arg: user_name - Name of the system user that will be create
-# | arg: home_dir - Path of the home dir for the user. Usually the final path of the app. If this argument is omitted, the user will be created without home
+# examples:
+# # Create a nextcloud user with no home directory and /usr/sbin/nologin login shell (hence no login capability)
+# ynh_system_user_create --username=nextcloud
+# # Create a discourse user using /var/www/discourse as home directory and the default login shell
+# ynh_system_user_create --username=discourse --home_dir=/var/www/discourse --use_shell
+#
+# usage: ynh_system_user_create --username=user_name [--home_dir=home_dir] [--use_shell]
+# | arg: -u, --username - Name of the system user that will be create
+# | arg: -h, --home_dir - Path of the home dir for the user. Usually the final path of the app. If this argument is omitted, the user will be created without home
+# | arg: -s, --use_shell - Create a user using the default login shell if present. If this argument is omitted, the user will be created with /usr/sbin/nologin shell
+#
+# Requires YunoHost version 2.6.4 or higher.
ynh_system_user_create () {
- if ! ynh_system_user_exists "$1" # Check if the user exists on the system
+ # Declare an array to define the options of this helper.
+ local legacy_args=uhs
+ declare -Ar args_array=( [u]=username= [h]=home_dir= [s]=use_shell )
+ local username
+ local home_dir
+ local use_shell
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ use_shell="${use_shell:-0}"
+ home_dir="${home_dir:-}"
+
+ if ! ynh_system_user_exists "$username" # Check if the user exists on the system
then # If the user doesn't exist
- if [ $# -ge 2 ]; then # If a home dir is mentioned
- local user_home_dir="-d $2"
+ if [ -n "$home_dir" ]; then # If a home dir is mentioned
+ local user_home_dir="-d $home_dir"
else
local user_home_dir="--no-create-home"
fi
- sudo useradd $user_home_dir --system --user-group $1 --shell /usr/sbin/nologin || ynh_die "Unable to create $1 system account"
+ if [ $use_shell -eq 1 ]; then # If we want a shell for the user
+ local shell="" # Use default shell
+ else
+ local shell="--shell /usr/sbin/nologin"
+ fi
+ useradd $user_home_dir --system --user-group $username $shell || ynh_die "Unable to create $username system account"
fi
}
# Delete a system user
#
-# usage: ynh_system_user_delete user_name
-# | arg: user_name - Name of the system user that will be create
+# usage: ynh_system_user_delete --username=user_name
+# | arg: -u, --username - Name of the system user that will be create
+#
+# Requires YunoHost version 2.6.4 or higher.
ynh_system_user_delete () {
- if ynh_system_user_exists "$1" # Check if the user exists on the system
+ # Declare an array to define the options of this helper.
+ local legacy_args=u
+ declare -Ar args_array=( [u]=username= )
+ local username
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ # Check if the user exists on the system
+ if ynh_system_user_exists "$username"
then
- echo "Remove the user $1" >&2
- sudo userdel $1
+ deluser $username
else
- echo "The user $1 was not found" >&2
+ ynh_print_warn --message="The user $username was not found"
+ fi
+
+ # Check if the group exists on the system
+ if ynh_system_group_exists "$username"
+ then
+ delgroup $username
fi
}
diff --git a/data/helpers.d/utils b/data/helpers.d/utils
index b280c3b21..e1feed6b1 100644
--- a/data/helpers.d/utils
+++ b/data/helpers.d/utils
@@ -1,107 +1,90 @@
-# Extract a key from a plain command output
-#
-# example: yunohost user info tata --output-as plain | ynh_get_plain_key mail
-#
-# usage: ynh_get_plain_key key [subkey [subsubkey ...]]
-# | ret: string - the key's value
-ynh_get_plain_key() {
- local prefix="#"
- local founded=0
- local key=$1
- shift
- while read line; do
- if [[ "$founded" == "1" ]] ; then
- [[ "$line" =~ ^${prefix}[^#] ]] && return
- echo $line
- elif [[ "$line" =~ ^${prefix}${key}$ ]]; then
- if [[ -n "${1:-}" ]]; then
- prefix+="#"
- key=$1
- shift
- else
- founded=1
- fi
- fi
- done
-}
+#!/bin/bash
-# Restore a previous backup if the upgrade process failed
+# Handle script crashes / failures
+#
+# [internal]
#
# usage:
-# ynh_backup_before_upgrade
+# ynh_exit_properly is used only by the helper ynh_abort_if_errors.
+# You should not use it directly.
+# Instead, add to your script:
# ynh_clean_setup () {
-# ynh_restore_upgradebackup
+# instructions...
# }
-# ynh_abort_if_errors
#
-ynh_restore_upgradebackup () {
- echo "Upgrade failed." >&2
- local app_bck=${app//_/-} # Replace all '_' by '-'
-
- NO_BACKUP_UPGRADE=${NO_BACKUP_UPGRADE:-0}
-
- if [ "$NO_BACKUP_UPGRADE" -eq 0 ]
- then
- # Check if an existing backup can be found before removing and restoring the application.
- if sudo yunohost backup list | grep -q $app_bck-pre-upgrade$backup_number
- then
- # Remove the application then restore it
- sudo yunohost app remove $app
- # Restore the backup
- sudo yunohost backup restore $app_bck-pre-upgrade$backup_number --apps $app --force
- ynh_die "The app was restored to the way it was before the failed upgrade."
- fi
- else
- echo "\$NO_BACKUP_UPGRADE is set, that means there's no backup to restore. You have to fix this upgrade by yourself !" >&2
- fi
-}
-
-# Make a backup in case of failed upgrade
+# This function provide a way to clean some residual of installation that not managed by remove script.
#
-# usage:
-# ynh_backup_before_upgrade
-# ynh_clean_setup () {
-# ynh_restore_upgradebackup
-# }
-# ynh_abort_if_errors
+# It prints a warning to inform that the script was failed, and execute the ynh_clean_setup function if used in the app script
#
-ynh_backup_before_upgrade () {
- if [ ! -e "/etc/yunohost/apps/$app/scripts/backup" ]
- then
- echo "This app doesn't have any backup script." >&2
- return
+ynh_exit_properly () {
+ local exit_code=$?
+ if [ "$exit_code" -eq 0 ]; then
+ exit 0 # Exit without error if the script ended correctly
fi
- backup_number=1
- local old_backup_number=2
- local app_bck=${app//_/-} # Replace all '_' by '-'
- NO_BACKUP_UPGRADE=${NO_BACKUP_UPGRADE:-0}
- if [ "$NO_BACKUP_UPGRADE" -eq 0 ]
- then
- # Check if a backup already exists with the prefix 1
- if sudo yunohost backup list | grep -q $app_bck-pre-upgrade1
- then
- # Prefix becomes 2 to preserve the previous backup
- backup_number=2
- old_backup_number=1
- fi
+ trap '' EXIT # Ignore new exit signals
+ set +eu # Do not exit anymore if a command fail or if a variable is empty
- # Create backup
- sudo BACKUP_CORE_ONLY=1 yunohost backup create --apps $app --name $app_bck-pre-upgrade$backup_number
- if [ "$?" -eq 0 ]
- then
- # If the backup succeeded, remove the previous backup
- if sudo yunohost backup list | grep -q $app_bck-pre-upgrade$old_backup_number
- then
- # Remove the previous backup only if it exists
- sudo yunohost backup delete $app_bck-pre-upgrade$old_backup_number > /dev/null
+ # Small tempo to avoid the next message being mixed up with other DEBUG messages
+ sleep 0.5
+
+ ynh_print_err --message="!!\n $app's script has encountered an error. Its execution was cancelled.\n!!"
+
+ # If the script is executed from the CLI, dump the end of the log that precedes the crash.
+ if [ "$YNH_INTERFACE" == "cli" ]
+ then
+ # Unset xtrace to not spoil the log
+ set +x
+
+ local ynh_log="/var/log/yunohost/yunohost-cli.log"
+
+ # Wait for the log to be fill with the data until the crash.
+ local timeout=0
+ while ! tail --lines=20 "$ynh_log" | grep --quiet "+ ynh_exit_properly"
+ do
+ ((timeout++))
+ if [ $timeout -eq 500 ]; then
+ break
fi
- else
- ynh_die "Backup failed, the upgrade process was aborted."
- fi
- else
- echo "\$NO_BACKUP_UPGRADE is set, backup will be avoided. Be careful, this upgrade is going to be operated without a security backup"
- fi
+ done
+
+ echo -e "\e[34m\e[1mPlease find here an extract of the log before the crash:\e[0m" >&2
+ # Tail the last 30 lines of log of YunoHost
+ # But remove all lines after "ynh_exit_properly"
+ # Remove the timestamp at the beginning of the line
+ # Remove "yunohost.hook..."
+ # Add DEBUG and color it at the beginning of each log line.
+ echo -e "$(tail --lines=30 "$ynh_log" \
+ | sed '1,/+ ynh_exit_properly/!d' \
+ | sed 's/^[[:digit:]: ,-]*//g' \
+ | sed 's/ *yunohost.hook.*\]/ -/g' \
+ | sed 's/^WARNING /&/g' \
+ | sed 's/^DEBUG /& /g' \
+ | sed 's/^INFO /& /g' \
+ | sed 's/^/\\e[34m\\e[1m[DEBUG]\\e[0m: /g')" >&2
+ set -x
+ fi
+
+ if type -t ynh_clean_setup > /dev/null; then # Check if the function exist in the app script.
+ ynh_clean_setup # Call the function to do specific cleaning for the app.
+ fi
+
+ ynh_die # Exit with error status
+}
+
+# Exits if an error occurs during the execution of the script.
+#
+# usage: ynh_abort_if_errors
+#
+# This configure the rest of the script execution such that, if an error occurs
+# or if an empty variable is used, the execution of the script stops
+# immediately and a call to `ynh_clean_setup` is triggered if it has been
+# defined by your script.
+#
+# Requires YunoHost version 2.6.4 or higher.
+ynh_abort_if_errors () {
+ set -eu # Exit if a command fail, and if a variable is used unset.
+ trap ynh_exit_properly EXIT # Capturing exit signals on shell script
}
# Download, check integrity, uncompress and patch the source from app.src
@@ -118,10 +101,12 @@ ynh_backup_before_upgrade () {
# SOURCE_FORMAT=tar.gz
# # (Optional) Put false if sources are directly in the archive root
# # default: true
+# # Instead of true, SOURCE_IN_SUBDIR could be the number of sub directories
+# # to remove.
# SOURCE_IN_SUBDIR=false
# # (Optionnal) Name of the local archive (offline setup support)
# # default: ${src_id}.${src_format}
-# SOURCE_FILENAME=example.tar.gz
+# SOURCE_FILENAME=example.tar.gz
# # (Optional) If it set as false don't extract the source.
# # (Useful to get a debian package or a python wheel.)
# # default: true
@@ -136,27 +121,44 @@ ynh_backup_before_upgrade () {
# If it's ok, the source archive will be uncompressed in $dest_dir. If the
# SOURCE_IN_SUBDIR is true, the first level directory of the archive will be
# removed.
+# If SOURCE_IN_SUBDIR is a numeric value, 2 for example, the 2 first level
+# directories will be removed
#
# Finally, patches named sources/patches/${src_id}-*.patch and extra files in
# sources/extra_files/$src_id will be applied to dest_dir
#
#
-# usage: ynh_setup_source dest_dir [source_id]
-# | arg: dest_dir - Directory where to setup sources
-# | arg: source_id - Name of the app, if the package contains more than one app
+# usage: ynh_setup_source --dest_dir=dest_dir [--source_id=source_id]
+# | arg: -d, --dest_dir - Directory where to setup sources
+# | arg: -s, --source_id - Name of the app, if the package contains more than one app
+#
+# Requires YunoHost version 2.6.4 or higher.
ynh_setup_source () {
- local dest_dir=$1
- local src_id=${2:-app} # If the argument is not given, source_id equals "app"
+ # Declare an array to define the options of this helper.
+ local legacy_args=ds
+ declare -Ar args_array=( [d]=dest_dir= [s]=source_id= )
+ local dest_dir
+ local source_id
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+ source_id="${source_id:-app}" # If the argument is not given, source_id equals "app"
+
+ local src_file_path="$YNH_CWD/../conf/${source_id}.src"
+ # In case of restore script the src file is in an other path.
+ # So try to use the restore path if the general path point to no file.
+ if [ ! -e "$src_file_path" ]; then
+ src_file_path="$YNH_CWD/../settings/conf/${source_id}.src"
+ fi
# Load value from configuration file (see above for a small doc about this file
# format)
- local src_url=$(grep 'SOURCE_URL=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
- local src_sum=$(grep 'SOURCE_SUM=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
- local src_sumprg=$(grep 'SOURCE_SUM_PRG=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
- local src_format=$(grep 'SOURCE_FORMAT=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
- local src_extract=$(grep 'SOURCE_EXTRACT=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
- local src_in_subdir=$(grep 'SOURCE_IN_SUBDIR=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
- local src_filename=$(grep 'SOURCE_FILENAME=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
+ local src_url=$(grep 'SOURCE_URL=' "$src_file_path" | cut -d= -f2-)
+ local src_sum=$(grep 'SOURCE_SUM=' "$src_file_path" | cut -d= -f2-)
+ local src_sumprg=$(grep 'SOURCE_SUM_PRG=' "$src_file_path" | cut -d= -f2-)
+ local src_format=$(grep 'SOURCE_FORMAT=' "$src_file_path" | cut -d= -f2-)
+ local src_extract=$(grep 'SOURCE_EXTRACT=' "$src_file_path" | cut -d= -f2-)
+ local src_in_subdir=$(grep 'SOURCE_IN_SUBDIR=' "$src_file_path" | cut -d= -f2-)
+ local src_filename=$(grep 'SOURCE_FILENAME=' "$src_file_path" | cut -d= -f2-)
# Default value
src_sumprg=${src_sumprg:-sha256sum}
@@ -165,7 +167,7 @@ ynh_setup_source () {
src_format=$(echo "$src_format" | tr '[:upper:]' '[:lower:]')
src_extract=${src_extract:-true}
if [ "$src_filename" = "" ] ; then
- src_filename="${src_id}.${src_format}"
+ src_filename="${source_id}.${src_format}"
fi
local local_src="/opt/yunohost-apps-src/${YNH_APP_ID}/${src_filename}"
@@ -173,56 +175,62 @@ ynh_setup_source () {
then # Use the local source file if it is present
cp $local_src $src_filename
else # If not, download the source
- local out=`wget -nv -O $src_filename $src_url 2>&1` || ynh_print_err $out
+ local out=`wget -nv -O $src_filename $src_url 2>&1` || ynh_print_err --message="$out"
fi
# Check the control sum
echo "${src_sum} ${src_filename}" | ${src_sumprg} -c --status \
- || ynh_die "Corrupt source"
+ || ynh_die --message="Corrupt source"
# Extract source into the app dir
mkdir -p "$dest_dir"
-
+
if ! "$src_extract"
then
mv $src_filename $dest_dir
elif [ "$src_format" = "zip" ]
- then
+ then
# Zip format
# Using of a temp directory, because unzip doesn't manage --strip-components
if $src_in_subdir ; then
local tmp_dir=$(mktemp -d)
unzip -quo $src_filename -d "$tmp_dir"
cp -a $tmp_dir/*/. "$dest_dir"
- ynh_secure_remove "$tmp_dir"
+ ynh_secure_remove --file="$tmp_dir"
else
unzip -quo $src_filename -d "$dest_dir"
fi
else
local strip=""
- if $src_in_subdir ; then
- strip="--strip-components 1"
+ if [ "$src_in_subdir" != "false" ]
+ then
+ if [ "$src_in_subdir" == "true" ]; then
+ local sub_dirs=1
+ else
+ local sub_dirs="$src_in_subdir"
+ fi
+ strip="--strip-components $sub_dirs"
fi
if [[ "$src_format" =~ ^tar.gz|tar.bz2|tar.xz$ ]] ; then
tar -xf $src_filename -C "$dest_dir" $strip
else
- ynh_die "Archive format unrecognized."
+ ynh_die --message="Archive format unrecognized."
fi
fi
# Apply patches
- if (( $(find $YNH_CWD/../sources/patches/ -type f -name "${src_id}-*.patch" 2> /dev/null | wc -l) > "0" )); then
+ if (( $(find $YNH_CWD/../sources/patches/ -type f -name "${source_id}-*.patch" 2> /dev/null | wc -l) > "0" )); then
local old_dir=$(pwd)
(cd "$dest_dir" \
- && for p in $YNH_CWD/../sources/patches/${src_id}-*.patch; do \
+ && for p in $YNH_CWD/../sources/patches/${source_id}-*.patch; do \
patch -p1 < $p; done) \
- || ynh_die "Unable to apply patches"
+ || ynh_die --message="Unable to apply patches"
cd $old_dir
fi
# Add supplementary files
- if test -e "$YNH_CWD/../sources/extra_files/${src_id}"; then
- cp -a $YNH_CWD/../sources/extra_files/$src_id/. "$dest_dir"
+ if test -e "$YNH_CWD/../sources/extra_files/${source_id}"; then
+ cp -a $YNH_CWD/../sources/extra_files/$source_id/. "$dest_dir"
fi
}
@@ -231,38 +239,47 @@ ynh_setup_source () {
# $domain and $path_url should be defined externally (and correspond to the domain.tld and the /path (of the app?))
#
# example: ynh_local_curl "/install.php?installButton" "foo=$var1" "bar=$var2"
-#
+#
# usage: ynh_local_curl "page_uri" "key1=value1" "key2=value2" ...
# | arg: page_uri - Path (relative to $path_url) of the page where POST data will be sent
# | arg: key1=value1 - (Optionnal) POST key and corresponding value
# | arg: key2=value2 - (Optionnal) Another POST key and corresponding value
# | arg: ... - (Optionnal) More POST keys and values
+#
+# Requires YunoHost version 2.6.4 or higher.
ynh_local_curl () {
- # Define url of page to curl
- local full_page_url=https://localhost$path_url$1
+ # Define url of page to curl
+ local local_page=$(ynh_normalize_url_path $1)
+ local full_path=$path_url$local_page
- # Concatenate all other arguments with '&' to prepare POST data
- local POST_data=""
- local arg=""
- for arg in "${@:2}"
- do
- POST_data="${POST_data}${arg}&"
- done
- if [ -n "$POST_data" ]
- then
- # Add --data arg and remove the last character, which is an unecessary '&'
- POST_data="--data ${POST_data::-1}"
- fi
-
- # Wait untils nginx has fully reloaded (avoid curl fail with http2)
- sleep 2
+ if [ "${path_url}" == "/" ]; then
+ full_path=$local_page
+ fi
- # Curl the URL
- curl --silent --show-error -kL -H "Host: $domain" --resolve $domain:443:127.0.0.1 $POST_data "$full_page_url"
+ local full_page_url=https://localhost$full_path
+
+ # Concatenate all other arguments with '&' to prepare POST data
+ local POST_data=""
+ local arg=""
+ for arg in "${@:2}"
+ do
+ POST_data="${POST_data}${arg}&"
+ done
+ if [ -n "$POST_data" ]
+ then
+ # Add --data arg and remove the last character, which is an unecessary '&'
+ POST_data="--data ${POST_data::-1}"
+ fi
+
+ # Wait untils nginx has fully reloaded (avoid curl fail with http2)
+ sleep 2
+
+ # Curl the URL
+ curl --silent --show-error -kL -H "Host: $domain" --resolve $domain:443:127.0.0.1 $POST_data "$full_page_url"
}
# Render templates with Jinja2
-#
+#
# Attention : Variables should be exported before calling this helper to be
# accessible inside templates.
#
@@ -278,3 +295,225 @@ ynh_render_template() {
jinja2.Template(sys.stdin.read()
).render(os.environ));' < $template_path > $output_path
}
+
+# Fetch the Debian release codename
+#
+# usage: ynh_get_debian_release
+# | ret: The Debian release codename (i.e. jessie, stretch, ...)
+#
+# Requires YunoHost version 2.7.12 or higher.
+ynh_get_debian_release () {
+ echo $(lsb_release --codename --short)
+}
+
+# Create a directory under /tmp
+#
+# [internal]
+#
+# Deprecated helper
+#
+# usage: ynh_mkdir_tmp
+# | ret: the created directory path
+ynh_mkdir_tmp() {
+ ynh_print_warn --message="The helper ynh_mkdir_tmp is deprecated."
+ ynh_print_warn --message="You should use 'mktemp -d' instead and manage permissions \
+properly with chmod/chown."
+ local TMP_DIR=$(mktemp -d)
+
+ # Give rights to other users could be a security risk.
+ # But for retrocompatibility we need it. (This helpers is deprecated)
+ chmod 755 $TMP_DIR
+ echo $TMP_DIR
+}
+
+# Remove a file or a directory securely
+#
+# usage: ynh_secure_remove --file=path_to_remove
+# | arg: -f, --file - File or directory to remove
+#
+# Requires YunoHost version 2.6.4 or higher.
+ynh_secure_remove () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=f
+ declare -Ar args_array=( [f]=file= )
+ local file
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ local forbidden_path=" \
+ /var/www \
+ /home/yunohost.app"
+
+ if [ $# -ge 2 ]
+ then
+ ynh_print_warn --message="/!\ Packager ! You provided more than one argument to ynh_secure_remove but it will be ignored... Use this helper with one argument at time."
+ fi
+
+ if [[ "$forbidden_path" =~ "$file" \
+ # Match all paths or subpaths in $forbidden_path
+ || "$file" =~ ^/[[:alnum:]]+$ \
+ # Match all first level paths from / (Like /var, /root, etc...)
+ || "${file:${#file}-1}" = "/" ]]
+ # Match if the path finishes by /. Because it seems there is an empty variable
+ then
+ ynh_print_warn --message="Avoid deleting $file."
+ else
+ if [ -e "$file" ]
+ then
+ sudo rm -R "$file"
+ else
+ ynh_print_info --message="$file wasn't deleted because it doesn't exist."
+ fi
+ fi
+}
+
+# Extract a key from a plain command output
+#
+# example: yunohost user info tata --output-as plain | ynh_get_plain_key mail
+#
+# usage: ynh_get_plain_key key [subkey [subsubkey ...]]
+# | ret: string - the key's value
+#
+# Requires YunoHost version 2.2.4 or higher.
+ynh_get_plain_key() {
+ local prefix="#"
+ local founded=0
+ # We call this key_ so that it's not caught as
+ # an info to be redacted by the core
+ local key_=$1
+ shift
+ while read line; do
+ if [[ "$founded" == "1" ]] ; then
+ [[ "$line" =~ ^${prefix}[^#] ]] && return
+ echo $line
+ elif [[ "$line" =~ ^${prefix}${key_}$ ]]; then
+ if [[ -n "${1:-}" ]]; then
+ prefix+="#"
+ key_=$1
+ shift
+ else
+ founded=1
+ fi
+ fi
+ done
+}
+
+# Read the value of a key in a ynh manifest file
+#
+# usage: ynh_read_manifest manifest key
+# | arg: -m, --manifest= - Path of the manifest to read
+# | arg: -k, --key= - Name of the key to find
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_read_manifest () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=mk
+ declare -Ar args_array=( [m]=manifest= [k]=manifest_key= )
+ local manifest
+ local manifest_key
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ if [ ! -e "$manifest" ]; then
+ # If the manifest isn't found, try the common place for backup and restore script.
+ manifest="../settings/manifest.json"
+ fi
+
+ jq ".$manifest_key" "$manifest" --raw-output
+}
+
+# Read the upstream version from the manifest
+#
+# The version number in the manifest is defined by ~ynh
+# For example : 4.3-2~ynh3
+# This include the number before ~ynh
+# In the last example it return 4.3-2
+#
+# usage: ynh_app_upstream_version [-m manifest]
+# | arg: -m, --manifest= - Path of the manifest to read
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_app_upstream_version () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=m
+ declare -Ar args_array=( [m]=manifest= )
+ local manifest
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ manifest="${manifest:-../manifest.json}"
+ version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version")
+ echo "${version_key/~ynh*/}"
+}
+
+# Read package version from the manifest
+#
+# The version number in the manifest is defined by ~ynh
+# For example : 4.3-2~ynh3
+# This include the number after ~ynh
+# In the last example it return 3
+#
+# usage: ynh_app_package_version [-m manifest]
+# | arg: -m, --manifest= - Path of the manifest to read
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_app_package_version () {
+ # Declare an array to define the options of this helper.
+ local legacy_args=m
+ declare -Ar args_array=( [m]=manifest= )
+ local manifest
+ # Manage arguments with getopts
+ ynh_handle_getopts_args "$@"
+
+ manifest="${manifest:-../manifest.json}"
+ version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version")
+ echo "${version_key/*~ynh/}"
+}
+
+# Checks the app version to upgrade with the existing app version and returns:
+#
+# - UPGRADE_APP if the upstream app version has changed
+# - UPGRADE_PACKAGE if only the YunoHost package has changed
+#
+# It stops the current script without error if the package is up-to-date
+#
+# This helper should be used to avoid an upgrade of an app, or the upstream part
+# of it, when it's not needed
+#
+# To force an upgrade, even if the package is up to date,
+# you have to set the variable YNH_FORCE_UPGRADE before.
+# example: sudo YNH_FORCE_UPGRADE=1 yunohost app upgrade MyApp
+#
+# usage: ynh_check_app_version_changed
+#
+# Requires YunoHost version 3.5.0 or higher.
+ynh_check_app_version_changed () {
+ local force_upgrade=${YNH_FORCE_UPGRADE:-0}
+ local package_check=${PACKAGE_CHECK_EXEC:-0}
+
+ # By default, upstream app version has changed
+ local return_value="UPGRADE_APP"
+
+ local current_version=$(ynh_read_manifest --manifest="/etc/yunohost/apps/$YNH_APP_INSTANCE_NAME/manifest.json" --manifest_key="version" || echo 1.0)
+ local current_upstream_version="$(ynh_app_upstream_version --manifest="/etc/yunohost/apps/$YNH_APP_INSTANCE_NAME/manifest.json")"
+ local update_version=$(ynh_read_manifest --manifest="../manifest.json" --manifest_key="version" || echo 1.0)
+ local update_upstream_version="$(ynh_app_upstream_version)"
+
+ if [ "$current_version" == "$update_version" ] ; then
+ # Complete versions are the same
+ if [ "$force_upgrade" != "0" ]
+ then
+ ynh_print_info --message="Upgrade forced by YNH_FORCE_UPGRADE."
+ unset YNH_FORCE_UPGRADE
+ elif [ "$package_check" != "0" ]
+ then
+ ynh_print_info --message="Upgrade forced for package check."
+ else
+ ynh_die "Up-to-date, nothing to do" 0
+ fi
+ elif [ "$current_upstream_version" == "$update_upstream_version" ] ; then
+ # Upstream versions are the same, only YunoHost package versions differ
+ return_value="UPGRADE_PACKAGE"
+ fi
+ echo $return_value
+}
diff --git a/data/hooks/backup/05-conf_ldap b/data/hooks/backup/05-conf_ldap
index b21103ede..9ae22095e 100755
--- a/data/hooks/backup/05-conf_ldap
+++ b/data/hooks/backup/05-conf_ldap
@@ -4,7 +4,7 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/conf/ldap"
diff --git a/data/hooks/backup/08-conf_ssh b/data/hooks/backup/08-conf_ssh
index ae422617e..ee976080c 100755
--- a/data/hooks/backup/08-conf_ssh
+++ b/data/hooks/backup/08-conf_ssh
@@ -4,7 +4,7 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/conf/ssh"
diff --git a/data/hooks/backup/11-conf_ynh_mysql b/data/hooks/backup/11-conf_ynh_mysql
index 60bd8c017..031707337 100755
--- a/data/hooks/backup/11-conf_ynh_mysql
+++ b/data/hooks/backup/11-conf_ynh_mysql
@@ -4,7 +4,7 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/conf/ynh/mysql"
diff --git a/data/hooks/backup/14-conf_ssowat b/data/hooks/backup/14-conf_ssowat
index ca42d3369..d4db72493 100755
--- a/data/hooks/backup/14-conf_ssowat
+++ b/data/hooks/backup/14-conf_ssowat
@@ -4,7 +4,7 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/conf/ssowat"
diff --git a/data/hooks/backup/17-data_home b/data/hooks/backup/17-data_home
index f7a797b6b..af00d67e8 100755
--- a/data/hooks/backup/17-data_home
+++ b/data/hooks/backup/17-data_home
@@ -4,7 +4,7 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/data/home"
diff --git a/data/hooks/backup/20-conf_ynh_firewall b/data/hooks/backup/20-conf_ynh_firewall
index 4e08114e7..98be3eb09 100755
--- a/data/hooks/backup/20-conf_ynh_firewall
+++ b/data/hooks/backup/20-conf_ynh_firewall
@@ -4,7 +4,7 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/conf/ynh/firewall"
diff --git a/data/hooks/backup/21-conf_ynh_certs b/data/hooks/backup/21-conf_ynh_certs
index f9687164d..a3912a995 100755
--- a/data/hooks/backup/21-conf_ynh_certs
+++ b/data/hooks/backup/21-conf_ynh_certs
@@ -4,7 +4,7 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/conf/ynh/certs"
diff --git a/data/hooks/backup/23-data_mail b/data/hooks/backup/23-data_mail
index 618a0aafe..7fdc883fd 100755
--- a/data/hooks/backup/23-data_mail
+++ b/data/hooks/backup/23-data_mail
@@ -4,7 +4,7 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/data/mail"
diff --git a/data/hooks/backup/26-conf_xmpp b/data/hooks/backup/26-conf_xmpp
index 12300a00a..b55ad2bfc 100755
--- a/data/hooks/backup/26-conf_xmpp
+++ b/data/hooks/backup/26-conf_xmpp
@@ -4,7 +4,7 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/conf/xmpp"
diff --git a/data/hooks/backup/29-conf_nginx b/data/hooks/backup/29-conf_nginx
index d900c7535..81e145e24 100755
--- a/data/hooks/backup/29-conf_nginx
+++ b/data/hooks/backup/29-conf_nginx
@@ -4,7 +4,7 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/conf/nginx"
diff --git a/data/hooks/backup/32-conf_cron b/data/hooks/backup/32-conf_cron
index 2fea9f53f..acbd009ab 100755
--- a/data/hooks/backup/32-conf_cron
+++ b/data/hooks/backup/32-conf_cron
@@ -4,12 +4,12 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/conf/cron"
# Backup the configuration
-for f in $(ls -1B /etc/cron.d/yunohost*); do
+for f in $(ls -1B /etc/cron.d/yunohost* 2> /dev/null); do
ynh_backup "$f" "${backup_dir}/${f##*/}"
done
diff --git a/data/hooks/backup/40-conf_ynh_currenthost b/data/hooks/backup/40-conf_ynh_currenthost
index e4a684576..6a98fd0d2 100755
--- a/data/hooks/backup/40-conf_ynh_currenthost
+++ b/data/hooks/backup/40-conf_ynh_currenthost
@@ -4,7 +4,7 @@
set -eu
# Source YNH helpers
-source /usr/share/yunohost/helpers.d/filesystem
+source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/conf/ynh"
diff --git a/data/hooks/conf_regen/02-ssl b/data/hooks/conf_regen/02-ssl
index 963ec12ef..1df3a3260 100755
--- a/data/hooks/conf_regen/02-ssl
+++ b/data/hooks/conf_regen/02-ssl
@@ -23,8 +23,10 @@ do_init_regen() {
mkdir -p "${ssl_dir}/"{ca,certs,crl,newcerts}
# initialize some files
+ # N.B. : the weird RANDFILE thing comes from:
+ # https://stackoverflow.com/questions/94445/using-openssl-what-does-unable-to-write-random-state-mean
[[ -f "${ssl_dir}/serial" ]] \
- || openssl rand -hex 19 > "${ssl_dir}/serial"
+ || RANDFILE=.rnd openssl rand -hex 19 > "${ssl_dir}/serial"
[[ -f "${ssl_dir}/index.txt" ]] \
|| touch "${ssl_dir}/index.txt"
@@ -76,6 +78,9 @@ do_init_regen() {
ln -sf "$ynh_crt" /etc/ssl/certs/yunohost_crt.pem
ln -sf "$ynh_key" /etc/ssl/private/yunohost_key.pem
fi
+
+ chown -R root:ssl-cert /etc/yunohost/certs/yunohost.org/
+ chmod o-rwx /etc/yunohost/certs/yunohost.org/
}
do_pre_regen() {
diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh
index 34cb441b4..54b7c55b7 100755
--- a/data/hooks/conf_regen/03-ssh
+++ b/data/hooks/conf_regen/03-ssh
@@ -2,7 +2,7 @@
set -e
-. /usr/share/yunohost/helpers.d/utils
+. /usr/share/yunohost/helpers
do_pre_regen() {
pending_dir=$1
@@ -12,23 +12,20 @@ do_pre_regen() {
[[ ! -f /etc/yunohost/from_script ]] || return 0
cd /usr/share/yunohost/templates/ssh
-
+
# do not listen to IPv6 if unavailable
[[ -f /proc/net/if_inet6 ]] && ipv6_enabled=true || ipv6_enabled=false
- # Support legacy setting (this setting might be disabled by a user during a migration)
- ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null)
- if [[ "$(yunohost settings get 'service.ssh.allow_deprecated_dsa_hostkey')" == "True" ]]; then
- ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null)"
- fi
-
- ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null)
+ ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null || true)
# Support legacy setting (this setting might be disabled by a user during a migration)
if [[ "$(yunohost settings get 'service.ssh.allow_deprecated_dsa_hostkey')" == "True" ]]; then
- ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null)"
+ ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null || true)"
fi
+ # Support different strategy for security configurations
+ export compatibility="$(yunohost settings get 'security.ssh.compatibility')"
+
export ssh_keys
export ipv6_enabled
ynh_render_template "sshd_config" "${pending_dir}/etc/ssh/sshd_config"
diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd
index d0a1fad63..4f7adda78 100755
--- a/data/hooks/conf_regen/06-slapd
+++ b/data/hooks/conf_regen/06-slapd
@@ -15,6 +15,7 @@ do_init_regen() {
# fix some permissions
chown root:openldap /etc/ldap/slapd.conf
chown -R openldap:openldap /etc/ldap/schema/
+ usermod -aG ssl-cert openldap
# check the slapd config file at first
slaptest -Q -u -f /etc/ldap/slapd.conf
@@ -43,7 +44,7 @@ do_pre_regen() {
|| touch "${pending_dir}/etc/ldap/slapd-yuno.conf"
# remove temporary backup file
- sudo rm -f "$tmp_backup_dir_file"
+ rm -f "$tmp_backup_dir_file"
# retrieve current and new backends
curr_backend=$(grep '^database' /etc/ldap/slapd.conf 2>/dev/null | awk '{print $2}')
@@ -52,15 +53,15 @@ do_pre_regen() {
# save current database before any conf changes
if [[ -n "$curr_backend" && "$curr_backend" != "$new_backend" ]]; then
backup_dir="/var/backups/dc=yunohost,dc=org-${curr_backend}-$(date +%s)"
- sudo mkdir -p "$backup_dir"
- sudo slapcat -b dc=yunohost,dc=org \
+ mkdir -p "$backup_dir"
+ slapcat -b dc=yunohost,dc=org \
-l "${backup_dir}/dc=yunohost-dc=org.ldif"
echo "$backup_dir" > "$tmp_backup_dir_file"
fi
# copy configuration files
cp -a ldap.conf slapd.conf "$ldap_dir"
- cp -a sudo.schema mailserver.schema "$schema_dir"
+ cp -a sudo.schema mailserver.schema yunohost.schema "$schema_dir"
install -D -m 644 slapd.default "${pending_dir}/etc/default/slapd"
}
@@ -69,24 +70,30 @@ do_post_regen() {
regen_conf_files=$1
# ensure that slapd.d exists
- sudo mkdir -p /etc/ldap/slapd.d
+ mkdir -p /etc/ldap/slapd.d
# fix some permissions
- sudo chown root:openldap /etc/ldap/slapd.conf
- sudo chown -R openldap:openldap /etc/ldap/schema/
- sudo chown -R openldap:openldap /etc/ldap/slapd.d/
+ echo "Making sure we have the right permissions needed ..."
+ # penldap user should be in the ssl-cert group to let it access the certificate for TLS
+ usermod -aG ssl-cert openldap
+ chown root:openldap /etc/ldap/slapd.conf
+ chown -R openldap:openldap /etc/ldap/schema/
+ chown -R openldap:openldap /etc/ldap/slapd.d/
+ chown -R root:ssl-cert /etc/yunohost/certs/yunohost.org/
+ chmod o-rwx /etc/yunohost/certs/yunohost.org/
[ -z "$regen_conf_files" ] && exit 0
# check the slapd config file at first
- sudo slaptest -Q -u -f /etc/ldap/slapd.conf
+ slaptest -Q -u -f /etc/ldap/slapd.conf
# check if a backup should be restored
backup_dir=$(cat "$tmp_backup_dir_file" 2>/dev/null || true)
if [[ -n "$backup_dir" && -f "${backup_dir}/dc=yunohost-dc=org.ldif" ]]; then
# regenerate LDAP config directory and import database as root
# since the admin user may be unavailable
- sudo sh -c "rm -Rf /etc/ldap/slapd.d;
+ echo "Regenerate LDAP config directory and import the database using slapadd"
+ sh -c "rm -Rf /etc/ldap/slapd.d;
mkdir /etc/ldap/slapd.d;
slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d;
chown -R openldap:openldap /etc/ldap/slapd.d;
@@ -95,13 +102,18 @@ do_post_regen() {
chown -R openldap:openldap /var/lib/ldap" 2>&1
else
# regenerate LDAP config directory from slapd.conf
- sudo rm -Rf /etc/ldap/slapd.d
- sudo mkdir /etc/ldap/slapd.d
- sudo slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/ 2>&1
- sudo chown -R openldap:openldap /etc/ldap/slapd.d/
+ echo "Regenerate LDAP config directory from slapd.conf"
+ rm -Rf /etc/ldap/slapd.d
+ mkdir /etc/ldap/slapd.d
+ slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/ 2>&1
+ chown -R openldap:openldap /etc/ldap/slapd.d/
fi
- sudo service slapd force-reload
+ echo "Running slapdindex"
+ su openldap -s "/bin/bash" -c "/usr/sbin/slapindex"
+
+ echo "Reloading slapd"
+ service slapd force-reload
# on slow hardware/vm this regen conf would exit before the admin user that
# is stored in ldap is available because ldap seems to slow to restart
diff --git a/data/hooks/conf_regen/15-nginx b/data/hooks/conf_regen/15-nginx
index 461c10c0c..59654a771 100755
--- a/data/hooks/conf_regen/15-nginx
+++ b/data/hooks/conf_regen/15-nginx
@@ -2,7 +2,7 @@
set -e
-. /usr/share/yunohost/helpers.d/utils
+. /usr/share/yunohost/helpers
do_init_regen() {
if [[ $EUID -ne 0 ]]; then
@@ -10,7 +10,25 @@ do_init_regen() {
exit 1
fi
- do_pre_regen ""
+ cd /usr/share/yunohost/templates/nginx
+
+ nginx_dir="/etc/nginx"
+ nginx_conf_dir="${nginx_dir}/conf.d"
+ mkdir -p "$nginx_conf_dir"
+
+ # install plain conf files
+ cp plain/* "$nginx_conf_dir"
+
+ # probably run with init: just disable default site, restart NGINX and exit
+ rm -f "${nginx_dir}/sites-enabled/default"
+
+ export compatibility="intermediate"
+ ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
+
+ # Restart nginx if conf looks good, otherwise display error and exit unhappy
+ nginx -t 2>/dev/null && service nginx restart || (nginx -t && exit 1)
+
+ exit 0
}
do_pre_regen() {
@@ -22,20 +40,16 @@ do_pre_regen() {
nginx_conf_dir="${nginx_dir}/conf.d"
mkdir -p "$nginx_conf_dir"
- # install plain conf files
+ # install / update plain conf files
cp plain/* "$nginx_conf_dir"
- # probably run with init: just disable default site, restart NGINX and exit
- if [[ -z "$pending_dir" ]]; then
- rm -f "${nginx_dir}/sites-enabled/default"
- service nginx restart
- exit 0
- fi
-
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host)
domain_list=$(sudo yunohost domain list --output-as plain --quiet)
+ # Support different strategy for security configurations
+ export compatibility="$(yunohost settings get 'security.nginx.compatibility')"
+
# add domain conf files
for domain in $domain_list; do
domain_conf_dir="${nginx_conf_dir}/${domain}.d"
@@ -58,6 +72,8 @@ do_pre_regen() {
done
+ ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
+
# remove old domain conf files
conf_files=$(ls -1 /etc/nginx/conf.d \
| awk '/^[^\.]+\.[^\.]+.*\.conf$/ { print $1 }')
diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix
index a3ad70327..b37425984 100755
--- a/data/hooks/conf_regen/19-postfix
+++ b/data/hooks/conf_regen/19-postfix
@@ -2,6 +2,8 @@
set -e
+. /usr/share/yunohost/helpers
+
do_pre_regen() {
pending_dir=$1
@@ -20,9 +22,12 @@ do_pre_regen() {
main_domain=$(cat /etc/yunohost/current_host)
domain_list=$(sudo yunohost domain list --output-as plain --quiet | tr '\n' ' ')
- cat main.cf \
- | sed "s/{{ main_domain }}/${main_domain}/g" \
- > "${postfix_dir}/main.cf"
+ # Support different strategy for security configurations
+ export compatibility="$(yunohost settings get 'security.postfix.compatibility')"
+
+ export main_domain
+ export domain_list
+ ynh_render_template "main.cf" "${postfix_dir}/main.cf"
cat postsrsd \
| sed "s/{{ main_domain }}/${main_domain}/g" \
diff --git a/data/hooks/conf_regen/34-mysql b/data/hooks/conf_regen/34-mysql
index 5ee91827b..8f7b5455e 100755
--- a/data/hooks/conf_regen/34-mysql
+++ b/data/hooks/conf_regen/34-mysql
@@ -1,7 +1,8 @@
#!/bin/bash
set -e
-MYSQL_PKG="mariadb-server-10.1"
+MYSQL_PKG="$(dpkg --list | sed -ne 's/^ii \(mariadb-server-[[:digit:].]\+\) .*$/\1/p')"
+. /usr/share/yunohost/helpers
do_pre_regen() {
pending_dir=$1
@@ -15,7 +16,6 @@ do_post_regen() {
regen_conf_files=$1
if [ ! -f /etc/yunohost/mysql ]; then
- . /usr/share/yunohost/helpers.d/string
# ensure that mysql is running
sudo systemctl -q is-active mysql.service \
@@ -25,8 +25,6 @@ do_post_regen() {
mysql_password=$(ynh_string_random 10)
sudo mysqladmin -s -u root -pyunohost password "$mysql_password" || {
if [ $FORCE -eq 1 ]; then
- . /usr/share/yunohost/helpers.d/package
-
echo "It seems that you have already configured MySQL." \
"YunoHost needs to have a root access to MySQL to runs its" \
"applications, and is going to reset the MySQL root password." \
diff --git a/data/hooks/conf_regen/43-dnsmasq b/data/hooks/conf_regen/43-dnsmasq
index 2c8ce797b..ed795c058 100755
--- a/data/hooks/conf_regen/43-dnsmasq
+++ b/data/hooks/conf_regen/43-dnsmasq
@@ -1,13 +1,11 @@
#!/bin/bash
set -e
+. /usr/share/yunohost/helpers
do_pre_regen() {
pending_dir=$1
- # source ip helpers
- . /usr/share/yunohost/helpers.d/ip
-
cd /usr/share/yunohost/templates/dnsmasq
# create directory for pending conf
diff --git a/data/hooks/restore/11-conf_ynh_mysql b/data/hooks/restore/11-conf_ynh_mysql
index 0aaaccd54..24cdb1e79 100644
--- a/data/hooks/restore/11-conf_ynh_mysql
+++ b/data/hooks/restore/11-conf_ynh_mysql
@@ -1,5 +1,7 @@
backup_dir="$1/conf/ynh/mysql"
-MYSQL_PKG="mariadb-server-10.1"
+MYSQL_PKG="$(dpkg --list | sed -ne 's/^ii \(mariadb-server-[[:digit:].]\+\) .*$/\1/p')"
+
+. /usr/share/yunohost/helpers
# ensure that mysql is running
service mysql status >/dev/null 2>&1 \
@@ -11,13 +13,11 @@ service mysql status >/dev/null 2>&1 \
new_pwd=$(sudo cat "${backup_dir}/root_pwd" || sudo cat "${backup_dir}/mysql")
[ -z "$curr_pwd" ] && curr_pwd="yunohost"
[ -z "$new_pwd" ] && {
- . /usr/share/yunohost/helpers.d/string
new_pwd=$(ynh_string_random 10)
}
# attempt to change it
sudo mysqladmin -s -u root -p"$curr_pwd" password "$new_pwd" || {
- . /usr/share/yunohost/helpers.d/package
echo "It seems that you have already configured MySQL." \
"YunoHost needs to have a root access to MySQL to runs its" \
diff --git a/data/other/dpkg-origins/yunohost b/data/other/dpkg-origins/yunohost
new file mode 100644
index 000000000..b3079a59c
--- /dev/null
+++ b/data/other/dpkg-origins/yunohost
@@ -0,0 +1,4 @@
+Vendor: YunoHost
+Vendor-URL: https://yunohost.org/
+Bugs: https://github.com/YunoHost/issues/
+Parent: Debian
diff --git a/data/other/ldap_scheme.yml b/data/other/ldap_scheme.yml
index 75bdea6e2..11504bbe8 100644
--- a/data/other/ldap_scheme.yml
+++ b/data/other/ldap_scheme.yml
@@ -17,6 +17,12 @@ parents:
- organizationalUnit
- top
+ ou=permission:
+ ou: permission
+ objectClass:
+ - organizationalUnit
+ - top
+
ou=groups:
ou: groups
objectClass:
@@ -29,22 +35,6 @@ parents:
- top
children:
- cn=admins,ou=groups:
- cn: admins
- gidNumber: "4001"
- memberUid: admin
- objectClass:
- - posixGroup
- - top
-
- cn=sftpusers,ou=groups:
- cn: sftpusers
- gidNumber: "4002"
- memberUid: admin
- objectClass:
- - posixGroup
- - top
-
cn=admin,ou=sudo:
cn: admin
sudoUser: admin
@@ -54,3 +44,34 @@ children:
objectClass:
- sudoRole
- top
+ cn=admins,ou=groups:
+ cn: admins
+ gidNumber: "4001"
+ memberUid: admin
+ objectClass:
+ - posixGroup
+ - top
+ cn=all_users,ou=groups:
+ cn: all_users
+ gidNumber: "4002"
+ objectClass:
+ - posixGroup
+ - groupOfNamesYnh
+
+depends_children:
+ cn=main.mail,ou=permission:
+ cn: main.mail
+ gidNumber: "5001"
+ objectClass:
+ - posixGroup
+ - permissionYnh
+ groupPermission:
+ - "cn=all_users,ou=groups,dc=yunohost,dc=org"
+ cn=main.metronome,ou=permission:
+ cn: main.metronome
+ gidNumber: "5002"
+ objectClass:
+ - posixGroup
+ - permissionYnh
+ groupPermission:
+ - "cn=all_users,ou=groups,dc=yunohost,dc=org"
diff --git a/data/templates/dnsmasq/plain/resolv.dnsmasq.conf b/data/templates/dnsmasq/plain/resolv.dnsmasq.conf
index bc36ef365..6b3bb95d3 100644
--- a/data/templates/dnsmasq/plain/resolv.dnsmasq.conf
+++ b/data/templates/dnsmasq/plain/resolv.dnsmasq.conf
@@ -9,23 +9,37 @@
# (FR) FDN
nameserver 80.67.169.12
+nameserver 2001:910:800::12
nameserver 80.67.169.40
+nameserver 2001:910:800::40
# (FR) LDN
nameserver 80.67.188.188
+nameserver 2001:913::8
# (FR) ARN
nameserver 89.234.141.66
+nameserver 2a00:5881:8100:1000::3
+# (FR) Aquilenet
+nameserver 185.233.100.100
+nameserver 2a0c:e300::100
+nameserver 185.233.100.101
+nameserver 2a0c:e300::101
# (FR) gozmail / grifon
-nameserver 89.234.186.18
+nameserver 80.67.190.200
+nameserver 2a00:5884:8218::1
# (DE) FoeBud / Digital Courage
nameserver 85.214.20.141
-# (FR) Aquilenet [added manually, following comments from @sachaz]
-nameserver 141.255.128.100
-nameserver 141.255.128.101
# (DE) CCC Berlin
-nameserver 213.73.91.35
+nameserver 195.160.173.53
+# (DE) AS250
+nameserver 194.150.168.168
+nameserver 2001:4ce8::53
# (DE) Ideal-Hosting
nameserver 84.200.69.80
+nameserver 2001:1608:10:25::1c04:b12f
nameserver 84.200.70.40
+nameserver 2001:1608:10:25::9249:d69b
# (DK) censurfridns
nameserver 91.239.100.100
+nameserver 2001:67c:28a4::
nameserver 89.233.43.71
+nameserver 2a01:3a0:53:53::
diff --git a/data/templates/dovecot/dovecot-ldap.conf b/data/templates/dovecot/dovecot-ldap.conf
index 221fe85c1..c7c9785fd 100644
--- a/data/templates/dovecot/dovecot-ldap.conf
+++ b/data/templates/dovecot/dovecot-ldap.conf
@@ -3,7 +3,7 @@ auth_bind = yes
ldap_version = 3
base = ou=users,dc=yunohost,dc=org
user_attrs = uidNumber=500,gidNumber=8,mailuserquota=quota_rule=*:bytes=%$
-user_filter = (&(objectClass=inetOrgPerson)(uid=%n))
-pass_filter = (&(objectClass=inetOrgPerson)(uid=%n))
+user_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
+pass_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
default_pass_scheme = SSHA
diff --git a/data/templates/fail2ban/jail.conf b/data/templates/fail2ban/jail.conf
index 05eb7e7a8..9b4d39f17 100644
--- a/data/templates/fail2ban/jail.conf
+++ b/data/templates/fail2ban/jail.conf
@@ -513,27 +513,27 @@ logpath = %(vsftpd_log)s
# ASSP SMTP Proxy Jail
[assp]
-port = smtp,465,submission
+port = smtp,submission
logpath = /root/path/to/assp/logs/maillog.txt
[courier-smtp]
-port = smtp,465,submission
+port = smtp,submission
logpath = %(syslog_mail)s
backend = %(syslog_backend)s
[postfix]
-port = smtp,465,submission
+port = smtp,submission
logpath = %(postfix_log)s
backend = %(postfix_backend)s
[postfix-rbl]
-port = smtp,465,submission
+port = smtp,submission
logpath = %(postfix_log)s
backend = %(postfix_backend)s
maxretry = 1
@@ -541,14 +541,14 @@ maxretry = 1
[sendmail-auth]
-port = submission,465,smtp
+port = submission,smtp
logpath = %(syslog_mail)s
backend = %(syslog_backend)s
[sendmail-reject]
-port = smtp,465,submission
+port = smtp,submission
logpath = %(syslog_mail)s
backend = %(syslog_backend)s
@@ -556,7 +556,7 @@ backend = %(syslog_backend)s
[qmail-rbl]
filter = qmail
-port = smtp,465,submission
+port = smtp,submission
logpath = /service/qmail/log/main/current
@@ -564,14 +564,14 @@ logpath = /service/qmail/log/main/current
# but can be set by syslog_facility in the dovecot configuration.
[dovecot]
-port = pop3,pop3s,imap,imaps,submission,465,sieve
+port = pop3,pop3s,imap,imaps,submission,sieve
logpath = %(dovecot_log)s
backend = %(dovecot_backend)s
[sieve]
-port = smtp,465,submission
+port = smtp,submission
logpath = %(dovecot_log)s
backend = %(dovecot_backend)s
@@ -584,19 +584,19 @@ logpath = %(solidpop3d_log)s
[exim]
-port = smtp,465,submission
+port = smtp,submission
logpath = %(exim_main_log)s
[exim-spam]
-port = smtp,465,submission
+port = smtp,submission
logpath = %(exim_main_log)s
[kerio]
-port = imap,smtp,imaps,465
+port = imap,smtp,imaps
logpath = /opt/kerio/mailserver/store/logs/security.log
@@ -607,14 +607,14 @@ logpath = /opt/kerio/mailserver/store/logs/security.log
[courier-auth]
-port = smtp,465,submission,imaps,pop3,pop3s
+port = smtp,submission,imaps,pop3,pop3s
logpath = %(syslog_mail)s
backend = %(syslog_backend)s
[postfix-sasl]
-port = smtp,465,submission,imap,imaps,pop3,pop3s
+port = smtp,submission,imap,imaps,pop3,pop3s
# You might consider monitoring /var/log/mail.warn instead if you are
# running postfix since it would provide the same log lines at the
# "warn" level but overall at the smaller filesize.
@@ -631,7 +631,7 @@ backend = %(syslog_backend)s
[squirrelmail]
-port = smtp,465,submission,imap,imap2,imaps,pop3,pop3s,http,https,socks
+port = smtp,submission,imap,imap2,imaps,pop3,pop3s,http,https,socks
logpath = /var/lib/squirrelmail/prefs/squirrelmail_access_log
diff --git a/data/templates/metronome/domain.tpl.cfg.lua b/data/templates/metronome/domain.tpl.cfg.lua
index 2c7fd7489..2ee9cfaae 100644
--- a/data/templates/metronome/domain.tpl.cfg.lua
+++ b/data/templates/metronome/domain.tpl.cfg.lua
@@ -8,7 +8,7 @@ VirtualHost "{{ domain }}"
hostname = "localhost",
user = {
basedn = "ou=users,dc=yunohost,dc=org",
- filter = "(&(objectClass=posixAccount)(mail=*@{{ domain }}))",
+ filter = "(&(objectClass=posixAccount)(mail=*@{{ domain }})(permission=cn=main.metronome,ou=permission,dc=yunohost,dc=org))",
usernamefield = "mail",
namefield = "cn",
},
diff --git a/data/templates/nginx/plain/global.conf b/data/templates/nginx/plain/global.conf
index ca8721afb..b3a5f356a 100644
--- a/data/templates/nginx/plain/global.conf
+++ b/data/templates/nginx/plain/global.conf
@@ -1,2 +1 @@
server_tokens off;
-gzip_types text/css text/javascript application/javascript;
diff --git a/data/templates/nginx/plain/yunohost_panel.conf.inc b/data/templates/nginx/plain/yunohost_panel.conf.inc
index 34afe136d..1c5a2d656 100644
--- a/data/templates/nginx/plain/yunohost_panel.conf.inc
+++ b/data/templates/nginx/plain/yunohost_panel.conf.inc
@@ -1,8 +1,8 @@
-# Insert YunoHost panel
-sub_filter '';
+# Insert YunoHost button + portal overlay
+sub_filter '';
sub_filter_once on;
# Apply to other mime types than text/html
sub_filter_types application/xhtml+xml;
# Prevent YunoHost panel files from being blocked by specific app rules
-location ~ ynhpanel\.(js|json|css) {
+location ~ (ynh_portal.js|ynh_overlay.css|ynh_userinfo.json) {
}
diff --git a/data/templates/nginx/server.tpl.conf b/data/templates/nginx/server.tpl.conf
index ee20c29c9..4a5e91557 100644
--- a/data/templates/nginx/server.tpl.conf
+++ b/data/templates/nginx/server.tpl.conf
@@ -1,3 +1,8 @@
+map $http_upgrade $connection_upgrade {
+ default upgrade;
+ '' close;
+}
+
server {
listen 80;
listen [::]:80;
@@ -12,7 +17,7 @@ server {
}
location /.well-known/autoconfig/mail/ {
- alias /var/www/.well-known/{{ domain }}/autoconfig/mail;
+ alias /var/www/.well-known/{{ domain }}/autoconfig/mail/;
}
access_log /var/log/nginx/{{ domain }}-access.log;
@@ -29,6 +34,14 @@ server {
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
+ {% if compatibility == "modern" %}
+ # Ciphers with modern compatibility
+ # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern
+ # The following configuration use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...)
+ ssl_protocols TLSv1.2;
+ ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
+ ssl_prefer_server_ciphers on;
+ {% else %}
# As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519
ssl_ecdh_curve secp521r1:secp384r1:prime256v1;
ssl_prefer_server_ciphers on;
@@ -38,20 +51,17 @@ server {
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
- # Ciphers with modern compatibility
- # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern
- # Uncomment the following to use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...)
- #ssl_protocols TLSv1.2;
- #ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
-
# Uncomment the following directive after DH generation
# > openssl dhparam -out /etc/ssl/private/dh2048.pem -outform PEM -2 2048
#ssl_dhparam /etc/ssl/private/dh2048.pem;
+ {% endif %}
# Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners
# https://wiki.mozilla.org/Security/Guidelines/Web_Security
# https://observatory.mozilla.org/
- more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload";
+ {% if domain_cert_ca != "Self-signed" %}
+ more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload";
+ {% endif %}
more_set_headers "Content-Security-Policy : upgrade-insecure-requests";
more_set_headers "Content-Security-Policy-Report-Only : default-src https: data: 'unsafe-inline' 'unsafe-eval'";
more_set_headers "X-Content-Type-Options : nosniff";
@@ -69,6 +79,10 @@ server {
resolver_timeout 5s;
{% endif %}
+ # Disable gzip to protect against BREACH
+ # Read https://trac.nginx.org/nginx/ticket/1720 (text/html cannot be disabled!)
+ gzip off;
+
access_by_lua_file /usr/share/ssowat/access.lua;
include /etc/nginx/conf.d/{{ domain }}.d/*.conf;
diff --git a/data/templates/nginx/plain/yunohost_admin.conf b/data/templates/nginx/yunohost_admin.conf
similarity index 65%
rename from data/templates/nginx/plain/yunohost_admin.conf
rename to data/templates/nginx/yunohost_admin.conf
index 3de66e3e6..e0d9f6bb1 100644
--- a/data/templates/nginx/plain/yunohost_admin.conf
+++ b/data/templates/nginx/yunohost_admin.conf
@@ -12,24 +12,24 @@ server {
}
server {
- # Disabling http2 for now as it's causing weird issues with curl
- #listen 443 ssl http2 default_server;
- #listen [::]:443 ssl http2 default_server;
- listen 443 ssl default_server;
- listen [::]:443 ssl default_server;
+ listen 443 ssl http2 default_server;
+ listen [::]:443 ssl http2 default_server;
ssl_certificate /etc/yunohost/certs/yunohost.org/crt.pem;
ssl_certificate_key /etc/yunohost/certs/yunohost.org/key.pem;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
+ {% if compatibility == "modern" %}
+ # Ciphers with modern compatibility
+ # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern
+ # Uncomment the following to use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...)
+ ssl_protocols TLSv1.2;
+ ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
+ ssl_prefer_server_ciphers on;
+ {% else %}
# As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519
- # (this doesn't work on jessie though ...?)
- # ssl_ecdh_curve secp521r1:secp384r1:prime256v1;
-
- # As suggested by https://cipherli.st/
- ssl_ecdh_curve secp384r1;
-
+ ssl_ecdh_curve secp521r1:secp384r1:prime256v1;
ssl_prefer_server_ciphers on;
# Ciphers with intermediate compatibility
@@ -37,27 +37,26 @@ server {
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
- # Ciphers with modern compatibility
- # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern
- # Uncomment the following to use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...)
- #ssl_protocols TLSv1.2;
- #ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
-
# Uncomment the following directive after DH generation
# > openssl dhparam -out /etc/ssl/private/dh2048.pem -outform PEM -2 2048
#ssl_dhparam /etc/ssl/private/dh2048.pem;
-
+ {% endif %}
+
# Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners
# https://wiki.mozilla.org/Security/Guidelines/Web_Security
- # https://observatory.mozilla.org/
- add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
- add_header 'Referrer-Policy' 'same-origin';
- add_header Content-Security-Policy "upgrade-insecure-requests; object-src 'none'; script-src https: 'unsafe-eval'";
- add_header X-Content-Type-Options nosniff;
- add_header X-XSS-Protection "1; mode=block";
- add_header X-Download-Options noopen;
- add_header X-Permitted-Cross-Domain-Policies none;
- add_header X-Frame-Options "SAMEORIGIN";
+ # https://observatory.mozilla.org/
+ more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload";
+ more_set_headers "Referrer-Policy : 'same-origin'";
+ more_set_headers "Content-Security-Policy : upgrade-insecure-requests; object-src 'none'; script-src https: 'unsafe-eval'";
+ more_set_headers "X-Content-Type-Options : nosniff";
+ more_set_headers "X-XSS-Protection : 1; mode=block";
+ more_set_headers "X-Download-Options : noopen";
+ more_set_headers "X-Permitted-Cross-Domain-Policies : none";
+ more_set_headers "X-Frame-Options : SAMEORIGIN";
+
+ # Disable gzip to protect against BREACH
+ # Read https://trac.nginx.org/nginx/ticket/1720 (text/html cannot be disabled!)
+ gzip off;
location / {
return 302 https://$http_host/yunohost/admin;
@@ -68,7 +67,8 @@ server {
if ($http_user_agent ~ (crawl|Googlebot|Slurp|spider|bingbot|tracker|click|parser|spider|facebookexternalhit) ) {
return 403;
}
-
+ # X-Robots-Tag to precise the rules applied.
+ add_header X-Robots-Tag "nofollow, noindex, noarchive, nosnippet";
# Redirect most of 404 to maindomain.tld/yunohost/sso
access_by_lua_file /usr/share/ssowat/access.lua;
}
diff --git a/data/templates/postfix/main.cf b/data/templates/postfix/main.cf
index c38896a3f..045b8edd0 100644
--- a/data/templates/postfix/main.cf
+++ b/data/templates/postfix/main.cf
@@ -33,7 +33,11 @@ smtpd_tls_key_file = /etc/yunohost/certs/{{ main_domain }}/key.pem
smtpd_tls_exclude_ciphers = aNULL, MD5, DES, ADH, RC4, 3DES
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtpd_tls_loglevel=1
+{% if compatibility == "intermediate" %}
smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3
+{% else %}
+smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1
+{% endif %}
smtpd_tls_mandatory_ciphers=high
smtpd_tls_eecdh_grade = ultra
@@ -58,7 +62,7 @@ alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydomain = {{ main_domain }}
mydestination = localhost
-relayhost =
+relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_command = procmail -a "$EXTENSION"
mailbox_size_limit = 0
@@ -68,71 +72,71 @@ inet_interfaces = all
#### Fit to the maximum message size to 30mb, more than allowed by GMail or Yahoo ####
message_size_limit = 31457280
-# Virtual Domains Control
-virtual_mailbox_domains = ldap:/etc/postfix/ldap-domains.cf
-virtual_mailbox_maps = ldap:/etc/postfix/ldap-accounts.cf
-virtual_mailbox_base =
-virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf
-virtual_alias_domains =
-virtual_minimum_uid = 100
-virtual_uid_maps = static:vmail
+# Virtual Domains Control
+virtual_mailbox_domains = ldap:/etc/postfix/ldap-domains.cf
+virtual_mailbox_maps = ldap:/etc/postfix/ldap-accounts.cf
+virtual_mailbox_base =
+virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf
+virtual_alias_domains =
+virtual_minimum_uid = 100
+virtual_uid_maps = static:vmail
virtual_gid_maps = static:mail
smtpd_sender_login_maps= ldap:/etc/postfix/ldap-accounts.cf
-# Dovecot LDA
-virtual_transport = dovecot
+# Dovecot LDA
+virtual_transport = dovecot
dovecot_destination_recipient_limit = 1
-# Enable SASL authentication for the smtpd daemon
-smtpd_sasl_auth_enable = yes
-smtpd_sasl_type = dovecot
-smtpd_sasl_path = private/auth
-# Fix some outlook's bugs
-broken_sasl_auth_clients = yes
-# Reject anonymous connections
-smtpd_sasl_security_options = noanonymous
+# Enable SASL authentication for the smtpd daemon
+smtpd_sasl_auth_enable = yes
+smtpd_sasl_type = dovecot
+smtpd_sasl_path = private/auth
+# Fix some outlook's bugs
+broken_sasl_auth_clients = yes
+# Reject anonymous connections
+smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =
-# Wait until the RCPT TO command before evaluating restrictions
-smtpd_delay_reject = yes
-
-# Basics Restrictions
-smtpd_helo_required = yes
-strict_rfc821_envelopes = yes
-
-# Requirements for the connecting server
-smtpd_client_restrictions =
- permit_mynetworks,
- permit_sasl_authenticated,
- reject_rbl_client bl.spamcop.net,
- reject_rbl_client cbl.abuseat.org,
- reject_rbl_client zen.spamhaus.org,
- permit
-
-# Requirements for the HELO statement
-smtpd_helo_restrictions =
- permit_mynetworks,
- permit_sasl_authenticated,
- reject_non_fqdn_hostname,
- reject_invalid_hostname,
- permit
-
-# Requirements for the sender address
+# Wait until the RCPT TO command before evaluating restrictions
+smtpd_delay_reject = yes
+
+# Basics Restrictions
+smtpd_helo_required = yes
+strict_rfc821_envelopes = yes
+
+# Requirements for the connecting server
+smtpd_client_restrictions =
+ permit_mynetworks,
+ permit_sasl_authenticated,
+ reject_rbl_client bl.spamcop.net,
+ reject_rbl_client cbl.abuseat.org,
+ reject_rbl_client zen.spamhaus.org,
+ permit
+
+# Requirements for the HELO statement
+smtpd_helo_restrictions =
+ permit_mynetworks,
+ permit_sasl_authenticated,
+ reject_non_fqdn_hostname,
+ reject_invalid_hostname,
+ permit
+
+# Requirements for the sender address
smtpd_sender_restrictions =
- reject_sender_login_mismatch,
- permit_mynetworks,
- permit_sasl_authenticated,
- reject_non_fqdn_sender,
+ reject_sender_login_mismatch,
+ permit_mynetworks,
+ permit_sasl_authenticated,
+ reject_non_fqdn_sender,
reject_unknown_sender_domain,
- permit
-
-# Requirement for the recipient address
-smtpd_recipient_restrictions =
- permit_mynetworks,
- permit_sasl_authenticated,
- reject_non_fqdn_recipient,
- reject_unknown_recipient_domain,
+ permit
+
+# Requirement for the recipient address
+smtpd_recipient_restrictions =
+ permit_mynetworks,
+ permit_sasl_authenticated,
+ reject_non_fqdn_recipient,
+ reject_unknown_recipient_domain,
reject_unauth_destination,
permit
@@ -154,3 +158,13 @@ smtpd_milters = inet:localhost:11332
# Skip email without checking if milter has died
milter_default_action = accept
+
+# Avoid to send simultaneously too many emails
+smtp_destination_concurrency_limit = 1
+default_destination_rate_delay = 5s
+
+# Avoid email adress scanning
+# By default it's possible to detect if the email adress exist
+# So it's easly possible to scan a server to know which email adress is valid
+# and after to send spam
+disable_vrfy_command = yes
\ No newline at end of file
diff --git a/data/templates/postfix/plain/ldap-accounts.cf b/data/templates/postfix/plain/ldap-accounts.cf
index bd3576dec..9f6f94e6d 100644
--- a/data/templates/postfix/plain/ldap-accounts.cf
+++ b/data/templates/postfix/plain/ldap-accounts.cf
@@ -1,5 +1,5 @@
server_host = localhost
server_port = 389
search_base = dc=yunohost,dc=org
-query_filter = (&(objectClass=mailAccount)(mail=%s))
+query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
result_attribute = uid
diff --git a/data/templates/postfix/plain/ldap-aliases.cf b/data/templates/postfix/plain/ldap-aliases.cf
index a9ef52cf9..5e7d3a6c1 100644
--- a/data/templates/postfix/plain/ldap-aliases.cf
+++ b/data/templates/postfix/plain/ldap-aliases.cf
@@ -1,5 +1,5 @@
server_host = localhost
server_port = 389
search_base = dc=yunohost,dc=org
-query_filter = (&(objectClass=mailAccount)(mail=%s))
+query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
result_attribute = maildrop
diff --git a/data/templates/postfix/plain/master.cf b/data/templates/postfix/plain/master.cf
index 2d8712604..377a90971 100644
--- a/data/templates/postfix/plain/master.cf
+++ b/data/templates/postfix/plain/master.cf
@@ -122,6 +122,6 @@ mailman unix - n n - - pipe
flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
${nexthop} ${user}
-# Dovecot LDA
-dovecot unix - n n - - pipe
- flags=DRhu user=vmail:mail argv=/usr/lib/dovecot/deliver -f ${sender} -d ${user}@${nexthop} -m ${extension}
+# Dovecot LDA
+dovecot unix - n n - - pipe
+ flags=DRhu user=vmail:mail argv=/usr/lib/dovecot/deliver -f ${sender} -d ${user}@${nexthop} -m ${extension} -a ${recipient}
diff --git a/data/templates/postfix/postsrsd b/data/templates/postfix/postsrsd
index 56bfd091e..a0451faf6 100644
--- a/data/templates/postfix/postsrsd
+++ b/data/templates/postfix/postsrsd
@@ -12,7 +12,7 @@ SRS_DOMAIN={{ main_domain }}
# the domain itself. Separate multiple domains by space or comma.
# We have to put some "dummy" stuff at start and end... see this comment :
# https://github.com/roehling/postsrsd/issues/64#issuecomment-284003762
-SRS_EXCLUDE_DOMAINS=dummy {{ domain_list }} dummy
+SRS_EXCLUDE_DOMAINS="dummy {{ domain_list }} dummy"
# First separator character after SRS0 or SRS1.
# Can be one of: -+=
diff --git a/data/templates/slapd/slapd.conf b/data/templates/slapd/slapd.conf
index 9a8800d9d..76f249060 100644
--- a/data/templates/slapd/slapd.conf
+++ b/data/templates/slapd/slapd.conf
@@ -14,6 +14,7 @@ include /etc/ldap/schema/nis.schema
include /etc/ldap/schema/inetorgperson.schema
include /etc/ldap/schema/mailserver.schema
include /etc/ldap/schema/sudo.schema
+include /etc/ldap/schema/yunohost.schema
# Where the pid file is put. The init.d script
# will not stop the server if you change this.
@@ -31,7 +32,7 @@ password-hash {SSHA}
# Where the dynamically loaded modules are stored
modulepath /usr/lib/ldap
moduleload back_mdb
-moduleload memberof
+moduleload memberof
# The maximum number of entries that is returned for a search operation
sizelimit 500
@@ -40,6 +41,10 @@ sizelimit 500
# for indexing.
tool-threads 1
+# TLS Support
+TLSCertificateFile /etc/yunohost/certs/yunohost.org/crt.pem
+TLSCertificateKeyFile /etc/yunohost/certs/yunohost.org/key.pem
+
#######################################################################
# Specific Backend Directives for mdb:
# Backend specific directives apply to this backend until another
@@ -63,9 +68,13 @@ suffix "dc=yunohost,dc=org"
directory "/var/lib/ldap"
# Indexing options for database #1
-index objectClass eq
-index uid eq,sub
-index entryCSN,entryUUID eq
+index objectClass eq
+index uid,sudoUser eq,sub
+index entryCSN,entryUUID eq
+index cn,mail eq
+index gidNumber,uidNumber eq
+index member,memberUid,uniqueMember eq
+index virtualdomain eq
# Save the time that the entry gets modified, for database #1
lastmod on
@@ -81,6 +90,7 @@ checkpoint 512 30
# These access lines apply to database #1 only
access to attrs=userPassword,shadowLastChange
by dn="cn=admin,dc=yunohost,dc=org" write
+ by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
by anonymous auth
by self write
by * none
@@ -90,6 +100,7 @@ access to attrs=userPassword,shadowLastChange
# Others should be able to see it.
access to attrs=cn,gecos,givenName,mail,maildrop,displayName,sn
by dn="cn=admin,dc=yunohost,dc=org" write
+ by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
by self write
by * read
@@ -108,5 +119,35 @@ access to dn.base="" by * read
# can read everything.
access to *
by dn="cn=admin,dc=yunohost,dc=org" write
+ by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
by group/groupOfNames/Member="cn=admin,ou=groups,dc=yunohost,dc=org" write
by * read
+
+# Configure Memberof Overlay (used for Yunohost permission)
+
+# Link user <-> group
+#dn: olcOverlay={0}memberof,olcDatabase={1}mdb,cn=config
+overlay memberof
+memberof-group-oc groupOfNamesYnh
+memberof-member-ad member
+memberof-memberof-ad memberOf
+memberof-dangling error
+memberof-refint TRUE
+
+# Link permission <-> groupes
+#dn: olcOverlay={1}memberof,olcDatabase={1}mdb,cn=config
+overlay memberof
+memberof-group-oc permissionYnh
+memberof-member-ad groupPermission
+memberof-memberof-ad permission
+memberof-dangling error
+memberof-refint TRUE
+
+# Link permission <-> user
+#dn: olcOverlay={2}memberof,olcDatabase={1}mdb,cn=config
+overlay memberof
+memberof-group-oc permissionYnh
+memberof-member-ad inheritPermission
+memberof-memberof-ad permission
+memberof-dangling error
+memberof-refint TRUE
diff --git a/data/templates/slapd/slapd.default b/data/templates/slapd/slapd.default
index 372b8f4ab..cb6f1b6d0 100644
--- a/data/templates/slapd/slapd.default
+++ b/data/templates/slapd/slapd.default
@@ -21,7 +21,7 @@ SLAPD_PIDFILE=
# sockets.
# Example usage:
# SLAPD_SERVICES="ldap://127.0.0.1:389/ ldaps:/// ldapi:///"
-SLAPD_SERVICES="ldap:/// ldapi:///"
+SLAPD_SERVICES="ldap://127.0.0.1:389/ ldaps:/// ldapi:///"
# If SLAPD_NO_START is set, the init script will not start or restart
# slapd (but stop will still work). Uncomment this if you are
diff --git a/data/templates/slapd/yunohost.schema b/data/templates/slapd/yunohost.schema
new file mode 100644
index 000000000..7da60a20c
--- /dev/null
+++ b/data/templates/slapd/yunohost.schema
@@ -0,0 +1,33 @@
+#dn: cn=yunohost,cn=schema,cn=config
+#objectClass: olcSchemaConfig
+#cn: yunohost
+# ATTRIBUTES
+# For Permission
+attributetype ( 1.3.6.1.4.1.17953.9.1.1 NAME 'permission'
+ DESC 'Yunohost permission on user and group side'
+ SUP distinguishedName )
+attributetype ( 1.3.6.1.4.1.17953.9.1.2 NAME 'groupPermission'
+ DESC 'Yunohost permission for a group on permission side'
+ SUP distinguishedName )
+attributetype ( 1.3.6.1.4.1.17953.9.1.3 NAME 'inheritPermission'
+ DESC 'Yunohost permission for user on permission side'
+ SUP distinguishedName )
+attributetype ( 1.3.6.1.4.1.17953.9.1.4 NAME 'URL'
+ DESC 'Yunohost application URL'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+# OBJECTCLASS
+# For Applications
+objectclass ( 1.3.6.1.4.1.17953.9.2.1 NAME 'groupOfNamesYnh'
+ DESC 'Yunohost user group'
+ SUP top AUXILIARY
+ MAY ( member $ businessCategory $ seeAlso $ owner $ ou $ o $ permission ) )
+objectclass ( 1.3.6.1.4.1.17953.9.2.2 NAME 'permissionYnh'
+ DESC 'a Yunohost application'
+ SUP top AUXILIARY
+ MUST cn
+ MAY ( groupPermission $ inheritPermission $ URL ) )
+# For User
+objectclass ( 1.3.6.1.4.1.17953.9.2.3 NAME 'userPermissionYnh'
+ DESC 'a Yunohost application'
+ SUP top AUXILIARY
+ MAY ( permission ) )
diff --git a/data/templates/ssh/sshd_config b/data/templates/ssh/sshd_config
index ed870e5dc..8dc0e8dfc 100644
--- a/data/templates/ssh/sshd_config
+++ b/data/templates/ssh/sshd_config
@@ -15,10 +15,17 @@ HostKey {{ key }}{% endfor %}
# https://infosec.mozilla.org/guidelines/openssh
# ##############################################
-# Keys, ciphers and MACS
-KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256
-Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
-MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com
+{% if compatibility == "intermediate" %}
+ KexAlgorithms diffie-hellman-group-exchange-sha256
+ Ciphers aes256-ctr,aes192-ctr,aes128-ctr
+ MACs hmac-sha2-512,hmac-sha2-256
+{% else %}
+ # By default use "modern" Mozilla configuration
+ # Keys, ciphers and MACS
+ KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256
+ Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
+ MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com
+{% endif %}
# Use kernel sandbox mechanisms where possible in unprivileged processes
UsePrivilegeSeparation sandbox
diff --git a/data/templates/yunohost/services.yml b/data/templates/yunohost/services.yml
index 62509e1e9..0d79b182f 100644
--- a/data/templates/yunohost/services.yml
+++ b/data/templates/yunohost/services.yml
@@ -20,8 +20,6 @@ mysql:
glances: {}
ssh:
log: /var/log/auth.log
-ssl:
- status: null
metronome:
log: [/var/log/metronome/metronome.log,/var/log/metronome/metronome.err]
slapd:
@@ -34,10 +32,9 @@ yunohost-firewall:
need_lock: true
nslcd:
log: /var/log/syslog
-nsswitch:
- status: null
-yunohost:
- status: null
+nsswitch: null
+ssl: null
+yunohost: null
bind9: null
tahoe-lafs: null
memcached: null
diff --git a/debian/changelog b/debian/changelog
index afedbdaeb..3eb347456 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,338 @@
+yunohost (3.6.4.6) stable; urgency=low
+
+ - [fix] Hopefully fix the issue about corrupted logs metadata files (d507d447, 1cec9d78)
+
+ -- Alexandre Aubin Mon, 05 Aug 2019 18:37:00 +0000
+
+yunohost (3.6.4.5) stable; urgency=low
+
+ - [fix] Typo in hotfix...
+
+ -- Alexandre Aubin Sun, 04 Aug 2019 18:45:00 +0000
+
+yunohost (3.6.4.4) stable; urgency=low
+
+ - [fix] Small typo breaking experimental config panel for apps (1224380)
+ - [mod] Remove the old ugly trick to change the admin password, not needed anymore (1cb0a26)
+ - [fix] Legit variable getting caught as an info to be redacted by the core (8212010)
+ - [fix] Exception handling for corrupted metadata about operation logs (#754)
+
+ Contributors: Aleks, Bram, ljf
+
+ -- Alexandre Aubin Sun, 04 Aug 2019 18:20:00 +0000
+
+yunohost (3.6.4.3) stable; urgency=low
+
+ - [hotfix] Fix some password-redacting cases that weren't caught up
+
+ -- Alexandre Aubin Sat, 06 Jul 2019 19:35:00 +0000
+
+yunohost (3.6.4.2) stable; urgency=low
+
+ - [hotfix] Use the acme-v02 API to fix the newAccount keyError in acme_tiny
+
+ -- Alexandre Aubin Sat, 06 Jul 2019 18:40:00 +0000
+
+yunohost (3.6.4.1) stable; urgency=low
+
+ - [hotfix] Slapd not being able to start on ipv4-only instances
+
+ -- Alexandre Aubin Fri, 05 Jul 2019 20:50:00 +0000
+
+yunohost (3.6.4) stable; urgency=low
+
+ Minor fixes + bumping version for stable release
+
+ -- Alexandre Aubin Thu, 04 Jul 2019 23:30:00 +0000
+
+yunohost (3.6.3) testing; urgency=low
+
+ - [fix] Less logging madness due ynh_script_progression building progress bar (#741)
+ - [fix] Update acme-tiny to 4.0.4 (#740)
+ - [fix] Missing old internet cube list in migration to unified apps.json (#745)
+ - [enh] Add manpage for Yunohost ! (#682)
+ - [enh] Config panel : use manifest.json/actions.json args format for config_panel.toml (#734)
+ - [enh] Allow to describe actions through toml file instead of json (#744)
+ - [mod] Proper return interface for app config panel (#739)
+ - [fix] Add mechanism to automatically detect and redact passwords from operation logs (#742)
+
+ Thanks to all contributors <3 ! (Aleks, Bram, ljf, toitoinebzh)
+
+ -- Alexandre Aubin Tue, 02 Jul 2019 11:10:00 +0000
+
+yunohost (3.6.2) testing; urgency=low
+
+ - [fix] Use systemd-run for more robust self-upgrade mechanism (158aa08)
+ - [enh] Add a do_not_backup_data app setting to avoid backing up data (#731)
+ - [enh] support config_panel in TOML format (#732)
+ - [fix] ynh_print_OFF when set -x is used in other helpers (#733)
+ - [enh] Add current and new version for apps in tools_update output (#735)
+ - [fix] Backup delete should delete symlink target (#738)
+ - [i18n] Improve translation for Occitan, French
+
+ Thanks to all contributors <3 ! (Aleks, Bram, kay0u, locness3, Maniack, Quentí)
+
+ -- Alexandre Aubin Mon, 24 Jun 2019 18:00:00 +0000
+
+yunohost (3.6.1.3) testing; urgency=low
+
+ - [fix] Missing quotes led to an issue during when upgrading postsrsd
+ - [fix] Running slapindex seems to fix the previous issues about LDAP indexing stuff
+
+ -- Alexandre Aubin Fri, 07 Jun 2019 06:38:00 +0000
+
+yunohost (3.6.1.2) testing; urgency=low
+
+ - [fix] More weird issues with slapd indexation ...
+ - [fix] Small issue with operation logging during failed upgrade (success status set to true)
+
+ -- Alexandre Aubin Wed, 05 Jun 2019 16:25:00 +0000
+
+yunohost (3.6.1.1) testing; urgency=low
+
+ - [fix] Weird issue in slapd triggered by indexing uidNumber / gidNumber
+
+ -- Alexandre Aubin Tue, 04 Jun 2019 15:10:00 +0000
+
+yunohost (3.6.1) testing; urgency=low
+
+ - [fix] current version in app_info (#730)
+ - [fix] Add indexes for fields listed by slapd in the logs (#729)
+ - [fix] Allow to display logs when postinstall fails (#728)
+ - [fix] Stupid issue with files inside tar : foo is not the same as ./foo (#726)
+ - [enh] Remove unecessary log messages (#724)
+ - [enh] Check for obvious conflict with already running apt/dpkg commands when running yunohost upgrade (d0c982a)
+
+ Thanks to all contributors <3 ! (Aleks, Kay0u, Bram, L. Murphy, MCMic)
+
+ -- Alexandre Aubin Tue, 04 Jun 2019 13:20:00 +0000
+
+yunohost (3.6.0) testing; urgency=low
+
+ ## Major changes
+
+ - [enh] Simplify the whole LDAP interface thing (#721)
+ - [enh] Rework how system upgrade is handled (#692)
+ - [enh] Properly reimplement bash completion for yunohost cli (#678)
+ - [enh] Migrate to apps.json / use it as default list (#666, #665)
+ - [enh] Decouple the regen-conf mechanism from services (#653)
+ - [i18n] Update translations for Catalan, Occitan, French, Italian, Spanish, Arabic
+
+ ## App helpers
+
+ - [mod] Set min version to 3.5.0 for helpers (#725)
+ - [enh] Add helpers for sso config (#720)
+ - [enh] Reorganize helpers (#717)
+ - [enh] Add the ongoing part to the progression bar when using ynh_script_progression (#715)
+ - [fix] postgresql helpers : force disconnection of all clients connected to the database (#713)
+ - [enh] Use printers in helpers (#712)
+ - [enh] Use ynh_systemd_action in helpers (#711)
+ - [fix] Fix extraction of weight value for ynh_script_progression (#710)
+ - [enh] Add support for ynh_setup_source in restore script (#703)
+
+ # Other changes
+
+ - [fix] Update censurfridns ipv6 (#727)
+ - [enh] Optimize ynh_script_progression (#723)
+ - [enh] Disable VRFY command in Postfix command (#722)
+ - [enh] Add a --with-details option for log list (#716)
+ - [enh] Specify -a parameter on dovecot lda for Sieve (#709)
+ - [fix] Fix an issue with config panels following changes in hook_exec (#707)
+ - [enh] Don't expose LDAP server to the outside world (#706)
+ - [fix] Remove backup hook warning about cron file (#704)
+ - [enh] Update nginx conf to handle WebSocket proxying (#701)
+ - [enh] Add size of apps in backup_info result (#699)
+ - [enh] Add a setting to remove support for TLSv1 and TLSv1.1 in Postfix (#696)
+ - [enh] Mark YunoHost as essential to avoid removing it inadvertenly (#694)
+ - [enh] Avoid to send simultaneously too many emails (#691)
+ - [enh] Dump log when an app script fails in CLI to help with debugging (#687)
+ - [fix] Many small technical fixes (ec48edf,251a338,d11d31d,3668bf7,c7eb5bb,9b08afc,cecaee4,95fdfb3,2bc0deb)
+
+ Thanks to all contributors : Aleks, Benoît, Bram, ButterflyOfFire, C. Vuillot, Josue, J. Maulny, Kayou, L. Noferini, Maniack, M. Thiel, Quentí, R. du Song, Sylkevicious, ljf, xaloc33, yalh76 ! <3
+
+ -- Alexandre Aubin Wed, 22 May 2019 19:10:00 +0000
+
+yunohost (3.5.2.2) stable; urgency=low
+
+ - Hotfix for ynh_psql_remove_db (from ljf)
+
+ -- Alexandre Aubin Thu, 18 Apr 2019 17:32:00 +0000
+
+yunohost (3.5.2.1) stable; urgency=low
+
+ - [fix] Fresh install was broken because of yunohost_admin.conf initialization
+
+ -- Alexandre Aubin Thu, 11 Apr 2019 14:38:00 +0000
+
+yunohost (3.5.2) stable; urgency=low
+
+ - Release as stable !
+ - [doc] Update script to automatically generate helper doc
+ - [i18n] Update translations for Catalan, Arabic, Italian
+
+ Thanks to all contributors: Aleks, xaloc, BoF, silkevicious ! <3
+
+ -- Alexandre Aubin Wed, 10 Apr 2019 01:53:00 +0000
+
+yunohost (3.5.1.1) testing; urgency=low
+
+ - [fix] enabled/disabled status for sysv services
+ - [fix] Nodejs helpers : use YNH_APP_INSTANCE_NAME instead of YNH_APP_ID (#700)
+ - [fix] nginx diagnosis when there's an error throwing a huge useless traceback. Use Popen instead to display the real error
+ - [fix] service_status returns different type of data if you ask for one or multiple services
+
+ -- Alexandre Aubin Wed, 03 Apr 2019 17:28:00 +0000
+
+yunohost (3.5.1) testing; urgency=low
+
+ - [fix] Fix the dbus interface to get info for services (#698)
+ - [mod] Use ask key for display_text instead and support i18n (#697)
+ - [fix] Rework tools update (#695)
+ - [enh] Nginx conf tweaks for theme (#689)
+ - [fix] Fix argument escaping in getopts (#685, #683)
+ - [enh] Support php versions in ynh_add_fpm_config (#674)
+ - [enh] Check that required services are up before running app install and upgrade (#670)
+ - [doc] Add min version for all helpers (#664)
+ - [enh] Add a setting to control compatibility/security tradeoff for nginx and ssh configurations (#640)
+ - [enh] Hooks to allow apps to extend the recommended DNS configuration (#517)
+ - Misc technical fixes / improvements (0bd781b, fad3edf, 1268872, 847ceca, 26e77b7, b6cff68)
+ - [i18n] Update translation for French, Catalan, Esperanto, Occitan
+
+ Thanks to all contributors: Aleks, Bram, Gabriel Corona, Jibec, Josue, Maniack C, Mélanie C., Quentí, Romuald du Song, ljf, ppr, Xaloc ! <3
+
+ -- Alexandre Aubin Wed, 03 Apr 2019 02:13:00 +0000
+
+yunohost (3.5.0.2) testing; urgency=low
+
+ - [fix] Make sure that `ynh_system_user_delete` also deletes the group (#680)
+ - [enh] `ynh_systemd_action` : reload-or-restart instead of just reload (#681)
+
+ Last minute fixes by Maniack ;)
+
+ -- Alexandre Aubin Thu, 14 Mar 2019 03:45:00 +0000
+
+yunohost (3.5.0.1) testing; urgency=low
+
+ - [fix] #675 introduced a bug in nginx conf ...
+
+ -- Alexandre Aubin Wed, 13 Mar 2019 19:23:00 +0000
+
+yunohost (3.5.0) testing; urgency=low
+
+ Core
+ ----
+
+ - [fix] Disable gzip entirely to avoid BREACH attacks (#675)
+ - [fix] Backup tests were broken (#673)
+ - [fix] Backup fails because output directory not empty (#672)
+ - [fix] Reject app password if they contains { or } (#671)
+ - [enh] Allow `display_text` 'fake' argument in manifest.json (#669)
+ - [fix] Optimize dyndns requests (#662)
+ - [enh] Don't add Strict-Transport-Security header in nginx conf if using a selfsigned cert (#661)
+ - [enh] Add apt-transport-https to dependencies (#658)
+ - [enh] Cache results from meltdown vulnerability checker (#656)
+ - [enh] Ensure the tar file is closed during the backup (#655)
+ - [enh] Be able to define hook to trigger when changing a setting (#654)
+ - [enh] Assert dpkg is not broken before app install (#652)
+ - [fix] Loading only one helper file leads to errors because missing getopts (#651)
+ - [enh] Improve / add some messages to improve UX (#650)
+ - [enh] Reload fail2ban instead of restart (#649)
+ - [enh] Add IPv6 resolvers from diyisp.org to resolv.dnsmasq.conf (#639)
+ - [fix] Remove old SMTP port (465) from fail2ban jail.conf (#637)
+ - [enh] Improve protection against indexation from the robots. (#622)
+ - [enh] Allow hooks to return data (#526)
+ - [fix] Do not make version number available from web API to unauthenticated users (#291)
+ - [i18n] Improve Russian and Chinese (Mandarin) translations
+
+ App helpers
+ -----------
+
+ - [enh] Optimize app setting helpers (#663, #676)
+ - [enh] Handle `ynh_install_nodejs` for arm64 / aarch64 (#660)
+ - [enh] Update postgresql helpers (#657)
+ - [enh] Print diff of files when backup by `ynh_backup_if_checksum_is_different` (#648)
+ - [enh] Add app debugger helper (#647)
+ - [fix] Escape double quote before eval in getopts (#646)
+ - [fix] `ynh_local_curl` not using the right url in some cases (#644)
+ - [fix] Get rid of annoying 'unable to initialize frontend' messages (#643)
+ - [enh] Check if dpkg is not broken when calling `ynh_wait_dpkg_free` (#638)
+ - [enh] Warn the packager that `ynh_secure_remove` should be used with only one arg… (#635, #642)
+ - [enh] Add `ynh_script_progression` helper (#634)
+ - [enh] Add `ynh_systemd_action` helper (#633)
+ - [enh] Allow to dig deeper into an archive with `ynh_setup_source` (#630)
+ - [enh] Use getops (#561)
+ - [enh] Add `ynh_check_app_version_changed` helper (#521)
+ - [enh] Add fail2ban helpers (#364)
+
+ Contributors: Alexandre Aubin, Jimmy Monin, Josué Tille, Kayou, Laurent Peuch, Lukas Fülling, Maniack Crudelis, Taekiro, frju365, ljf, opi, yalh76, Алексей
+
+ -- Alexandre Aubin Wed, 13 Mar 2019 16:10:00 +0000
+
+yunohost (3.4.2.4) stable; urgency=low
+
+ - [fix] Meltdown vulnerability checker something outputing trash instead of pure json
+
+ -- Alexandre Aubin Tue, 19 Feb 2019 19:11:38 +0000
+
+yunohost (3.4.2.3) stable; urgency=low
+
+ - [fix] Admin password appearing in logs after logging in on webadmin
+ - [fix] Update friendly DNS resolver list
+
+ -- Alexandre Aubin Thu, 07 Feb 2019 03:20:10 +0000
+
+yunohost (3.4.2.2) stable; urgency=low
+
+ - Silly bug in migraton 8 :|
+
+ -- Alexandre Aubin Wed, 30 Jan 2019 21:17:00 +0000
+
+yunohost (3.4.2.1) stable; urgency=low
+
+ Small issues
+ - Fix parsing of the Meltdown vulnerability checker (ignore stderr :/)
+ - Mail autoconfig was broken, follow-up of #564
+ - Handle the fact that the archive folder might not exist, in migration 0008
+
+ -- Alexandre Aubin Wed, 30 Jan 2019 16:37:00 +0000
+
+yunohost (3.4.2) stable; urgency=low
+
+ - [fix] Do not log stretch migration in /tmp/ (#632)
+ - [fix] Some issues with ynh_handle_getopts_args (#628)
+ - [fix] Revert some stuff about separates php-ini file (c.f. #548) (#627)
+ - [fix] App conflicted with itself during change_url (#626)
+ - [fix] Improve `ynh_package_install_from_equivs` debuggability (#625)
+ - [enh] Add systemd log handling (#624)
+ - [enh] Update spectre meltdown checker (#620)
+ - [fix] Propagate HTTP2, more_set_headers and ecdh_curve changes to webadmin (#618)
+ - [enh] Control the login shell when creating users in ynh_system_user_create (#455, #629)
+ - [fix] Postgresql-9.4 was being detected as installed whereas it was in fact not (969577b)
+ - [fix] Restoring system failed because of temporary dumb password being refused (51712f9)
+
+ Thanks to all contributors (Aleks, frju365, JimboJoe, kay0u, Maniack, opi) ! <3
+
+ -- Alexandre Aubin Tue, 29 Jan 2019 16:42:00 +0000
+
+yunohost (3.4.1) testing; urgency=low
+
+ * [fix] `_run_service_command` not properly returning False if command fails (#616)
+ * [enh] Change git clone for gitlab working with branch (#615)
+ * [fix] Set owner of archives folder to 'admin' (#613)
+ * [enh] Add reload and restart actions to 'yunohost service' (#611)
+ * [fix] propagate --no-checks cert-install option to renew crontab (#610)
+ * [fix] Several issues with bootprompt (#609)
+ * [fix] Fix the way change_url updates the domain/path (#608)
+ * [fix] Repair tests (#607)
+ * [fix] Explicit dependance to iptables (1667ba1)
+ * [i18n] Tiny typographic changes (#612)
+ * [i18n] Improve translations for Hungarian, Esperanto, German
+ * Misc minor fixes and improvements.
+
+ Thanks to all contributors (Aleks, Bram, J. Meggyeshazi, Jibec, Josué, M. Martin, P. Bourré, anubis) ! <3
+
+ -- Alexandre Aubin Thu, 17 Jan 2019 22:16:00 +0000
+
yunohost (3.4.0) testing; urgency=low
* Misc fixes (#601, #600, #593)
@@ -24,6 +359,12 @@ yunohost (3.4.0) testing; urgency=low
-- Alexandre Aubin Thu, 20 Dec 2018 22:13:00 +0000
+yunohost (3.3.4) stable; urgency=low
+
+ * [fix] Use --force-confold and noninteractive debian frontend during core upgrade (#614)
+
+ -- Alexandre Aubin Thu, 17 Jan 2019 02:00:00 +0000
+
yunohost (3.3.3) stable; urgency=low
* [fix] ynh_wait_dpkg_free displaying a warning despite everything being okay (#593)
diff --git a/debian/control b/debian/control
index 9f72bf11a..64c7cd31d 100644
--- a/debian/control
+++ b/debian/control
@@ -2,25 +2,27 @@ Source: yunohost
Section: utils
Priority: extra
Maintainer: YunoHost Contributors
-Build-Depends: debhelper (>=9), dh-systemd, dh-python, python-all (>= 2.7)
+Build-Depends: debhelper (>=9), dh-systemd, dh-python, python-all (>= 2.7), python-yaml, python-jinja2
Standards-Version: 3.9.6
X-Python-Version: >= 2.7
Homepage: https://yunohost.org/
Package: yunohost
+Essential: yes
Architecture: all
Depends: ${python:Depends}, ${misc:Depends}
, moulinette (>= 2.7.1), ssowat (>= 2.7.1)
, python-psutil, python-requests, python-dnspython, python-openssl
, python-apt, python-miniupnpc, python-dbus, python-jinja2
- , glances
+ , python-toml
+ , glances, apt-transport-https
, dnsutils, bind9utils, unzip, git, curl, cron, wget, jq
, ca-certificates, netcat-openbsd, iproute
, mariadb-server, php-mysql | php-mysqlnd
, slapd, ldap-utils, sudo-ldap, libnss-ldapd, unscd, libpam-ldapd
, postfix-ldap, postfix-policyd-spf-perl, postfix-pcre, procmail, mailutils, postsrsd
, dovecot-ldap, dovecot-lmtpd, dovecot-managesieved
- , dovecot-antispam, fail2ban
+ , dovecot-antispam, fail2ban, iptables
, nginx-extras (>=1.6.2), php-fpm, php-ldap, php-intl
, dnsmasq, openssl, avahi-daemon, libnss-mdns, resolvconf, libnss-myhostname
, metronome
diff --git a/debian/install b/debian/install
index b540ca749..e0743cdd1 100644
--- a/debian/install
+++ b/debian/install
@@ -1,10 +1,12 @@
bin/* /usr/bin/
sbin/* /usr/sbin/
data/bash-completion.d/yunohost /etc/bash_completion.d/
+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/password/* /usr/share/yunohost/other/password/
+data/other/dpkg-origins/yunohost /etc/dpkg/origins
data/other/* /usr/share/yunohost/yunohost-config/moulinette/
data/templates/* /usr/share/yunohost/templates/
data/helpers /usr/share/yunohost/
diff --git a/debian/postinst b/debian/postinst
index df7112b9d..7b7080513 100644
--- a/debian/postinst
+++ b/debian/postinst
@@ -12,7 +12,7 @@ do_configure() {
bash /usr/share/yunohost/hooks/conf_regen/15-nginx init
else
echo "Regenerating configuration, this might take a while..."
- yunohost service regen-conf --output-as none
+ yunohost tools regen-conf --output-as none
echo "Launching migrations.."
yunohost tools migrations migrate --auto
@@ -23,6 +23,12 @@ do_configure() {
|| echo "yunohost-firewall service is not running, you should " \
"consider to start it by doing 'service yunohost-firewall start'."
fi
+
+ # Change dpkg vendor
+ # see https://wiki.debian.org/Derivatives/Guidelines#Vendor
+ readlink -f /etc/dpkg/origins/default | grep -q debian \
+ && rm -f /etc/dpkg/origins/default \
+ && ln -s /etc/dpkg/origins/yunohost /etc/dpkg/origins/default
# Yunoprompt
systemctl enable yunoprompt.service
diff --git a/debian/postrm b/debian/postrm
index 93338c4ff..63e42b4d4 100644
--- a/debian/postrm
+++ b/debian/postrm
@@ -14,6 +14,10 @@ if [ "$1" = "remove" ]; then
rm -f /etc/yunohost/installed
fi
+# Reset dpkg vendor to debian
+# see https://wiki.debian.org/Derivatives/Guidelines#Vendor
+rm -f /etc/dpkg/origins/default
+ln -s /etc/dpkg/origins/debian /etc/dpkg/origins/default
#DEBHELPER#
diff --git a/debian/rules b/debian/rules
index ce03d0e31..8afe372b5 100755
--- a/debian/rules
+++ b/debian/rules
@@ -7,6 +7,11 @@
%:
dh ${@} --with=python2,systemd
+override_dh_auto_build:
+ # Generate bash completion file
+ python data/actionsmap/yunohost_completion.py
+ python doc/generate_manpages.py --gzip --output doc/yunohost.8.gz
+
override_dh_installinit:
dh_installinit -pyunohost --name=yunohost-api --restart-after-upgrade
dh_installinit -pyunohost --name=yunohost-firewall --noscripts
diff --git a/doc/generate_helper_doc.py b/doc/generate_helper_doc.py
index 7d8c489b7..a7866b85b 100644
--- a/doc/generate_helper_doc.py
+++ b/doc/generate_helper_doc.py
@@ -4,7 +4,12 @@ import os
import glob
import datetime
-def render(data):
+def render(helpers):
+
+ data = { "helpers": helpers,
+ "date": datetime.datetime.now().strftime("%m/%d/%Y"),
+ "version": open("../debian/changelog").readlines()[0].split()[1].strip("()")
+ }
from jinja2 import Template
from ansi2html import Ansi2HTMLConverter
@@ -43,7 +48,7 @@ class Parser():
"code": [] }
for i, line in enumerate(self.file):
-
+
if line.startswith("#!/bin/bash"):
continue
@@ -65,6 +70,18 @@ class Parser():
# We're still in a comment bloc
assert line.startswith("# ") or line == "#", malformed_error(i)
current_block["comments"].append(line[2:])
+ elif line.strip() == "":
+ # Well eh that was not an actual helper definition ... start over ?
+ current_reading = "void"
+ current_block = { "name": None,
+ "line": -1,
+ "comments": [],
+ "code": []
+ }
+ elif not (line.endswith("{") or line.endswith("()")):
+ # Well we're not actually entering a function yet eh
+ # (c.f. global vars)
+ pass
else:
# We're getting out of a comment bloc, we should find
# the name of the function
@@ -74,8 +91,6 @@ class Parser():
# Then we expect to read the function
current_reading = "code"
- continue
-
elif current_reading == "code":
if line == "}":
@@ -92,7 +107,6 @@ class Parser():
"code": [] }
else:
current_block["code"].append(line)
- pass
continue
@@ -103,7 +117,6 @@ class Parser():
b["usage"] = ""
b["args"] = []
b["ret"] = ""
- b["example"] = ""
subblocks = '\n'.join(b["comments"]).split("\n\n")
@@ -114,17 +127,29 @@ class Parser():
b["brief"] = subblock
continue
- elif subblock.startswith("example"):
+ elif subblock.startswith("example:"):
b["example"] = " ".join(subblock.split()[1:])
continue
+ elif subblock.startswith("examples:"):
+ b["examples"] = subblock.split("\n")[1:]
+ continue
+
elif subblock.startswith("usage"):
for line in subblock.split("\n"):
if line.startswith("| arg"):
- argname = line.split()[2]
- argdescr = " ".join(line.split()[4:])
- b["args"].append((argname, argdescr))
+ linesplit = line.split()
+ argname = linesplit[2]
+ # Detect that there's a long argument version (-f, --foo - Some description)
+ if argname.endswith(",") and linesplit[3].startswith("--"):
+ argname = argname.strip(",")
+ arglongname = linesplit[3]
+ argdescr = " ".join(linesplit[5:])
+ b["args"].append((argname, arglongname, argdescr))
+ else:
+ argdescr = " ".join(linesplit[4:])
+ b["args"].append((argname, argdescr))
elif line.startswith("| ret"):
b["ret"] = " ".join(line.split()[2:])
else:
@@ -136,9 +161,17 @@ class Parser():
elif subblock.startswith("| arg"):
for line in subblock.split("\n"):
if line.startswith("| arg"):
- argname = line.split()[2]
- argdescr = line.split()[4:]
- b["args"].append((argname, argdescr))
+ linesplit = line.split()
+ argname = linesplit[2]
+ # Detect that there's a long argument version (-f, --foo - Some description)
+ if argname.endswith(",") and linesplit[3].startswith("--"):
+ argname = argname.strip(",")
+ arglongname = linesplit[3]
+ argdescr = " ".join(linesplit[5:])
+ b["args"].append((argname, arglongname, argdescr))
+ else:
+ argdescr = " ".join(linesplit[4:])
+ b["args"].append((argname, argdescr))
continue
else:
diff --git a/doc/generate_manpages.py b/doc/generate_manpages.py
new file mode 100644
index 000000000..0b1251c28
--- /dev/null
+++ b/doc/generate_manpages.py
@@ -0,0 +1,85 @@
+"""
+Inspired by yunohost_completion.py (author: Christophe Vuillot)
+=======
+
+This script generates man pages for yunohost.
+Pages are stored in OUTPUT_DIR
+"""
+
+import os
+import yaml
+import gzip
+import argparse
+
+from datetime import date
+from collections import OrderedDict
+
+from jinja2 import Template
+
+base_path = os.path.split(os.path.realpath(__file__))[0]
+
+template = Template(open(os.path.join(base_path, "manpage.template")).read())
+
+
+THIS_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+ACTIONSMAP_FILE = os.path.join(THIS_SCRIPT_DIR, '../data/actionsmap/yunohost.yml')
+
+
+def ordered_yaml_load(stream):
+ class OrderedLoader(yaml.Loader):
+ pass
+ OrderedLoader.add_constructor(
+ yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
+ lambda loader, node: OrderedDict(loader.construct_pairs(node)))
+ return yaml.load(stream, OrderedLoader)
+
+
+def main():
+ parser = argparse.ArgumentParser(description="generate yunohost manpage based on actionsmap.yml")
+ parser.add_argument("-o", "--output", default="output/yunohost")
+ parser.add_argument("-z", "--gzip", action="store_true", default=False)
+
+ args = parser.parse_args()
+
+ if os.path.isdir(args.output):
+ if not os.path.exists(args.output):
+ os.makedirs(args.output)
+
+ output_path = os.path.join(args.output, "yunohost")
+ else:
+ output_dir = os.path.split(args.output)[0]
+
+ if output_dir and not os.path.exists(output_dir):
+ os.makedirs(output_dir)
+
+ output_path = args.output
+
+ # man pages of "yunohost *"
+ with open(ACTIONSMAP_FILE, 'r') as actionsmap:
+
+ # Getting the dictionary containning what actions are possible per domain
+ actionsmap = ordered_yaml_load(actionsmap)
+
+ for i in actionsmap.keys():
+ if i.startswith("_"):
+ del actionsmap[i]
+
+ today = date.today()
+
+ result = template.render(
+ month=today.strftime("%B"),
+ year=today.year,
+ categories=actionsmap,
+ str=str,
+ )
+
+ if not args.gzip:
+ with open(output_path, "w") as output:
+ output.write(result)
+ else:
+ with gzip.open(output_path, mode="w", compresslevel=9) as output:
+ output.write(result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/helper_doc_template.html b/doc/helper_doc_template.html
index 1fa1f68ad..92611c737 100644
--- a/doc/helper_doc_template.html
+++ b/doc/helper_doc_template.html
@@ -2,7 +2,7 @@
App helpers
-{% for category, helpers in data %}
+{% for category, helpers in data.helpers %}
{{ category }}
@@ -27,8 +27,12 @@
Arguments:
- {% for name, descr in h.args %}
-
{{ name }} : {{ descr }}
+ {% for infos in h.args %}
+ {% if infos|length == 2 %}
+
{{ infos[0] }} : {{ infos[1] }}
+ {% else %}
+
{{ infos[0] }}, {{ infos[1] }} : {{ infos[2] }}
+ {% endif %}
{% endfor %}
@@ -38,11 +42,25 @@
Returns: {{ h.ret }}
{% endif %}
- {% if h.example %}
+ {% if "example" in h.keys() %}
Example: {{ h.example }}
{% endif %}
+ {% if "examples" in h.keys() %}
+
+ Examples:
+ {% for example in h.examples %}
+ {% if not example.strip().startswith("# ") %}
+ {{ example }}
+ {% else %}
+ {{ example.strip("# ") }}
+ {% endif %}
+
+ {% endfor %}
+