From dca7bee1ecbc671832b019e497ba058bbb6bde44 Mon Sep 17 00:00:00 2001 From: Jephte CLAIN Date: Thu, 19 Nov 2015 10:24:40 +0400 Subject: [PATCH 1/9] =?UTF-8?q?pxs=20fonctionne=20pour=20les=20d=C3=A9p?= =?UTF-8?q?=C3=B4t=20normaux=20comme=20pci=20-A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uproject | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/uproject b/uproject index c6c2284..66be44b 100755 --- a/uproject +++ b/uproject @@ -78,11 +78,15 @@ COMMANDS xadd xunlock xdrop - xsync xwhereis xwebapp Chacune de ces commandes est un raccourci vers la commande correspondante de git annex, sans le préfixe 'x' + xsync + Sur un dépot où git-annex est activé, lancer 'git annex sync' si on est + en mode indirect ou 'git annex sync --content' si on est en mode direct. + Sur un dépôt où git-annex n'est pas activé, faire l'équivalent des + commandes 'git add -A && git commit && git push' xcopy xmove xget @@ -237,7 +241,9 @@ elif array_contains VCS_CMDS "$CMD"; then elif array_contains GITANNEX_CMDS "$CMD"; then function xsync() { - if is_yes "$(git config --get annex.direct)"; then + if ! git_have_annex; then + git_commit -A "Maj des fichiers" + elif is_yes "$(git config --get annex.direct)"; then git annex add && git annex sync && git annex sync --content && From c2d5eab6c139394fcf0a3b4911a63785d4dec021 Mon Sep 17 00:00:00 2001 From: Jephte CLAIN Date: Thu, 19 Nov 2015 10:31:04 +0400 Subject: [PATCH 2/9] =?UTF-8?q?pxs=20fait=20aussi=20git=20pull=20avec=20un?= =?UTF-8?q?=20d=C3=A9p=C3=B4t=20sans=20annexe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uproject | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uproject b/uproject index 66be44b..0f97ad7 100755 --- a/uproject +++ b/uproject @@ -86,7 +86,7 @@ COMMANDS Sur un dépot où git-annex est activé, lancer 'git annex sync' si on est en mode indirect ou 'git annex sync --content' si on est en mode direct. Sur un dépôt où git-annex n'est pas activé, faire l'équivalent des - commandes 'git add -A && git commit && git push' + commandes 'git add -A && git commit && git pull && git push' xcopy xmove xget @@ -242,7 +242,7 @@ elif array_contains VCS_CMDS "$CMD"; then elif array_contains GITANNEX_CMDS "$CMD"; then function xsync() { if ! git_have_annex; then - git_commit -A "Maj des fichiers" + git_commit -Al "Maj des fichiers" && git pull && git_push elif is_yes "$(git config --get annex.direct)"; then git annex add && git annex sync && From 7c6ad6a08bf0306cd8ccff8a9f4c6c7ed76c0759 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 7 Dec 2015 04:43:46 +0400 Subject: [PATCH 3/9] =?UTF-8?q?apacheconfig:=20nouvelles=20options=20pour?= =?UTF-8?q?=20le=20d=C3=A9veloppement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apacheconfig | 50 +++++++++++++-- lib/ulib/apache.tools | 139 ++++++++++++++++++++++++++++++++---------- 2 files changed, 150 insertions(+), 39 deletions(-) diff --git a/apacheconfig b/apacheconfig index efe1a97..7a17742 100755 --- a/apacheconfig +++ b/apacheconfig @@ -47,7 +47,21 @@ OPTIONS Les arguments qui restent sont passés tels quels à apache_autoconf -r, --certsdir CERTSDIR Spécifier le cas échéant le répertoire contenant les certificats à - déployer. Cet argument est requis si le répertoire certsconf/ existe." + déployer. Cet argument est requis si le répertoire certsconf/ existe. + + -C, --one-conf CONF + Ne déployer que le fichier de configuration spécifié. Cette option + implique --deploy et est utile pour le développement et les tests. + -M, --one-module MODULE + Ne déployer que le fichier de module spécifié. Cette option implique + --deploy et est utile pour le développement et les tests. + -S, --one-site SITE + Ne déployer que le fichier de site spécifié. Cette option implique + --deploy et est utile pour le développement et les tests. + --localhosts + Créer dans le fichier /etc/hosts tous les noms d'hôte ayant un suffixe + .local mentionnés dans les fichiers de site. Cette option est utile pour + le développement et les tests." } # toujours placer une variable dépendante AVANT la variable maitre @@ -139,6 +153,9 @@ destdir= nohideconfig= templateopt= aac_certsdir= +oneconf= +onemodule= +onesite= args=( --help '$exit_with display_help' -c,--create action=create @@ -160,6 +177,10 @@ args=( -8,--jessie '$array_add TEMPLATECTL_VARS sysver=jessie' -u,--update,--deploy action=deploy -r:,--certsdir: aac_certsdir= + -C:,--one-conf: '$set@ oneconf; action=deploy' + -M:,--one-module: '$set@ onemodule; action=deploy' + -S:,--one-site: '$set@ onesite; action=deploy' + --localhosts action=localhosts ) parse_args "$@"; set -- "${args[@]}" @@ -218,21 +239,38 @@ $__TEMPLATECTL_HELP" templatectl -d "$destdir" --config "$config" --no-load-vars ${templateopt:+-$templateopt} "$@" ################################################################################ -elif [ "$action" == deploy ]; then +elif [ "$action" == deploy -o "$action" == localhosts ]; then [ -d "$destdir" ] || die "$destdir: répertoire introuvable" - args=(-d "$destdir" --deploy ${aac_certsdir:+-r "$aac_certsdir"}) + args=( + -d "$destdir" --$action ${aac_certsdir:+-r "$aac_certsdir"} + ${oneconf:+--one-conf "$oneconf"} + ${onemodule:+--one-module "$onemodule"} + ${onesite:+--one-site "$onesite"} + ) for __name in "${TEMPLATE_DYNAMIC_VARS[@]}"; do array_add args -v "$__name=${!__name}" done array_add args -- "$@" run_as_root "${args[@]}" - etitle "Mise à jour du système" - args=("$destdir" "$aac_certsdir") + if [ "$action" == deploy ]; then + etitle "Mise à jour du système" + args=( + apache_autoconf + --ignore "$(basename -- "$config")" + ${oneconf:+--one-conf "$(basename -- "$oneconf")"} + ${onemodule:+--one-module "$(basename -- "$onemodule")"} + ${onesite:+--one-site "$(basename -- "$onesite")"} + ) + elif [ "$action" == localhosts ]; then + etitle "Mise à jour de /etc/hosts" + args=(apache_autoconf_localhosts) + fi + args=("${args[@]}" "$destdir" "$aac_certsdir") for __name in "${TEMPLATE_DYNAMIC_VARS[@]}"; do array_add args "$__name=${!__name}" done - apache_autoconf --ignore "$(basename -- "$config")" "${args[@]}" "$@" || die + "${args[@]}" "$@" || die eend fi diff --git a/lib/ulib/apache.tools b/lib/ulib/apache.tools index 73fc0d0..a10496a 100644 --- a/lib/ulib/apache.tools +++ b/lib/ulib/apache.tools @@ -155,6 +155,27 @@ OPTIONS return 0 } +function __apache_autoconf_setup() { + if ! check_sysinfos -s linux -d debian; then + eerror "apache_autoconf n'est supporté que sur Debian linux" + return 1 + fi + urequire install + compute_apache_prefixes + return 0 +} +function __apache_autoconf_fillxxx() { + local var name value first=1 + for var in "$@"; do + splitvar "$var" name value + array_addu FILLVARS "$name" + [ -n "$first" ] || FILLSCRIPT="$FILLSCRIPT"$'\n' + FILLSCRIPT="${FILLSCRIPT}s/@@${name}@@/$(qseds "${value}")/g" + first= + done + # Il faut un fichier temporaire pour les remplacement de fichiers + ac_set_tmpfile FILLTEMP +} function __apache_autoconf_fillcopy() { # copier le fichier $1 vers le fichier $2. Si le fichier $1 contient l'une # des variables du tableau $FILLVARS, corriger d'abord le fichier avec le @@ -186,14 +207,14 @@ __APACHE_AUTOCONF_HELP="\ fichiers 'confs.conf', 'modules.conf' et 'sites.conf'. Par défaut, prendre le répertoire local DESTDIR. --confsdir CONFSDIR - Spécifier l'emplacement des fichiers de configuration des configuration. Par - défaut, utiliser DESTDIR/confs si ce répertoire existe. + Spécifier l'emplacement des fichiers des configuration. Par défaut, utiliser + DESTDIR/confs si ce répertoire existe. --modulesdir MODULESDIR - Spécifier l'emplacement des fichiers de configuration des modules. Par - défaut, utiliser DESTDIR/modules si ce répertoire existe. + Spécifier l'emplacement des fichiers des modules. Par défaut, utiliser + DESTDIR/modules si ce répertoire existe. --sitesdir SITESDIR - Spécifier l'emplacement des fichiers de configuration des sites. Par défaut, - utiliser DESTDIR/sites si ce répertoire existe. + Spécifier l'emplacement des fichiers des sites. Par défaut, utiliser + DESTDIR/sites si ce répertoire existe. --cgibindir CGIBINDIR Spécifier l'emplacement des scripts cgi à installer. Par défaut, utiliser DESTDIR/cgi-bin si ce répertoire existe. @@ -213,15 +234,19 @@ function __display_apache_autoconf_help() { eecho "$__APACHE_AUTOCONF_HELP"; } function apache_autoconf() { eval "$(utools_local)" local -a ignores - local autoconfdir certsdir confdir confsdir modulesdir sitesdir cgibindir wwwdir certsconfdir rrdir + local autoconfdir certsdir confdir confsdir oneconf modulesdir onemodule + local sitesdir onesite cgibindir wwwdir certsconfdir rrdir onecms local restart=1 parse_opts "${PRETTYOPTS[@]}" \ --help '$exit_with __display_apache_autoconf_help' \ --ignore: ignores \ --confdir: confdir= \ --confsdir: confsdir= \ + --one-conf: oneconf= \ --modulesdir: modulesdir= \ + --one-module: onemodule= \ --sitesdir: sitesdir= \ + --one-site: onesite= \ --cgibindir: cgibindir= \ --wwwdir: wwwdir= \ --certsconfdir: certsconfdir= \ @@ -229,12 +254,7 @@ function apache_autoconf() { --no-restart restart= \ @ args -- "$@" && set -- "${args[@]}" || die "$args" - if ! check_sysinfos -s linux -d debian; then - eerror "apache_autoconf n'est supporté que sur Debian linux" - return 1 - fi - urequire install - compute_apache_prefixes + __apache_autoconf_setup || return 1 # Configuration autoconfdir="$1"; shift @@ -259,20 +279,12 @@ function apache_autoconf() { [ -n "$wwwdir" ] || wwwdir="$autoconfdir/www" [ -n "$certsconfdir" ] || certsconfdir="$autoconfdir/certsconf" [ -n "$rrdir" ] || rrdir="$autoconfdir/RewriteRules" + [ -n "$oneconf" -o -n "$onemodule" -o -n "$onesite" ] && onecms=1 # Faire un script sed pour remplacer les variables spécifiées par leur # valeur dans les fichiers local -a FILLVARS; local FILLSCRIPT FILLTEMP - local var name value first=1 - for var in "$@"; do - splitvar "$var" name value - array_addu FILLVARS "$name" - [ -n "$first" ] || FILLSCRIPT="$FILLSCRIPT"$'\n' - FILLSCRIPT="${FILLSCRIPT}s/@@${name}@@/$(qseds "${value}")/g" - first= - done - # Il faut un fichier temporaire pour les remplacement de fichiers - ac_set_tmpfile FILLTEMP + __apache_autoconf_fillxxx # Copie des certificats local modified rehash conf @@ -308,12 +320,14 @@ function apache_autoconf() { fi # Gestion des configurations - if [ -d "$confsdir" ]; then + if [ -d "$confsdir" -a \( -z "$onecms" -o "$oneconf" \) ]; then local -a confs local conf etitle "Installation des configurations" array_from_lines confs "$(list_files "$confsdir" "*.conf")" for conf in "${confs[@]}"; do + [ -z "$oneconf" -o "$conf" == "$oneconf" ] || continue + estep "$conf" __apache_autoconf_fillcopy \ "$confsdir/$conf" \ @@ -323,12 +337,14 @@ function apache_autoconf() { fi # Gestion des modules - if [ -d "$modulesdir" ]; then + if [ -d "$modulesdir" -a \( -z "$onecms" -o "$onemodule" \) ]; then local -a confs local conf etitle "Installation des configurations des modules" array_from_lines confs "$(list_files "$modulesdir" "*.conf")" for conf in "${confs[@]}"; do + [ -z "$onemodule" -o "$conf" == "$onemodule" ] || continue + estep "$conf" __apache_autoconf_fillcopy \ "$modulesdir/$conf" \ @@ -338,7 +354,7 @@ function apache_autoconf() { fi # Règles de réécriture - if [ -d "$rrdir" ]; then + if [ -d "$rrdir" -a -z "$onecms" ]; then local -a confs local conf etitle "Installation des règles de réécriture" @@ -354,18 +370,19 @@ function apache_autoconf() { # Sites local -a enablesites disablesites - if [ -d "$sitesdir" ]; then + if [ -d "$sitesdir" -a \( -z "$onecms" -o "$onesite" \) ]; then local -a confs local conf confname destconf certsconf etitle "Installation des sites" array_lsfiles confs "$sitesdir" "*.conf" for conf in "${confs[@]}"; do confname="$(basename "$conf")" - destconf="$confname" + [ -z "$onesite" -o "$confname" == "$onesite" ] || continue + certsconf= - if [ "${destconf%.ssl.conf}" != "$destconf" ]; then + if [ "${confname%.ssl.conf}" != "$confname" ]; then if [ -d "$certsconfdir" ]; then - certsconf="${destconf%.ssl.conf}-certs.conf" + certsconf="${confname%.ssl.conf}-certs.conf" else ewarn "$conf: fichier ignoré parce que --certsconfdir n'a pas été spécifié" fi @@ -373,6 +390,7 @@ function apache_autoconf() { case "$destconf" in default.conf) destconf=default;; default.ssl.conf) destconf=default-ssl;; + *) destconf="$confname";; esac if [ -n "$certsconf" ]; then @@ -401,7 +419,7 @@ s#@@ca@@#$APACHESSLCERTSDIR/$(basename "$ca")#g fi # Fichiers de configuration - if [ -d "$confdir" ]; then + if [ -d "$confdir" -a -z "$onecms" ]; then local -a confs local conf etitle "Configuration de base" @@ -474,14 +492,14 @@ s#@@ca@@#$APACHESSLCERTSDIR/$(basename "$ca")#g fi # Scripts CGI - if [ -d "$cgibindir" ]; then + if [ -d "$cgibindir" -a -z "$onecms" ]; then etitle "Installation des scripts CGI" cpdirnovcs "$cgibindir" "$CGIBINDIR" eend fi # Contenu web - if [ -d "$wwwdir" ]; then + if [ -d "$wwwdir" -a -z "$onecms" ]; then etitle "Installation des fichiers du serveur web" cpdirnovcs "$wwwdir" "$HTDOCSDIR" eend @@ -512,3 +530,58 @@ s#@@ca@@#$APACHESSLCERTSDIR/$(basename "$ca")#g fi fi } + +function apache_autoconf_localhosts() { + eval "$(utools_local)" + local autoconfdir sitesdir + parse_opts "${PRETTYOPTS[@]}" \ + --sitesdir: sitesdir= \ + @ args -- "$@" && set -- "${args[@]}" || die "$args" + + __apache_autoconf_setup || return 1 + + # Configuration + autoconfdir="$1"; shift + [ -n "$autoconfdir" ] || { + eerror "Vous devez spécifier le répertoire de base de la configuration apache" + return 1 + } + [ -n "$sitesdir" ] || sitesdir="$autoconfdir/sites" + + # Faire un script sed pour remplacer les variables spécifiées par leur + # valeur dans les fichiers + local -a FILLVARS; local FILLSCRIPT FILLTEMP ULIB_INSTALL_VERBOSE + __apache_autoconf_fillxxx + + local -a newhosts + if [ -d "$sitesdir" ]; then + local -a localips localhosts confs candidates newhosts + local conf tmpconf candidate + + array_from_lines localips "$(>/etc/hosts + done +} From 26fe7097f69f22a6108c54185a5aa39d368b277d Mon Sep 17 00:00:00 2001 From: Jephte CLAIN Date: Mon, 7 Dec 2015 11:14:29 +0400 Subject: [PATCH 4/9] =?UTF-8?q?apacheconfig:=20-S=20peut=20aussi=20=C3=AAt?= =?UTF-8?q?re=20utilis=C3=A9=20avec=20--localhosts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apacheconfig | 36 +++++++++++++++++++----------------- lib/ulib/apache.tools | 8 ++++++-- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/apacheconfig b/apacheconfig index 7a17742..2284e32 100755 --- a/apacheconfig +++ b/apacheconfig @@ -49,19 +49,20 @@ OPTIONS Spécifier le cas échéant le répertoire contenant les certificats à déployer. Cet argument est requis si le répertoire certsconf/ existe. - -C, --one-conf CONF - Ne déployer que le fichier de configuration spécifié. Cette option - implique --deploy et est utile pour le développement et les tests. - -M, --one-module MODULE - Ne déployer que le fichier de module spécifié. Cette option implique - --deploy et est utile pour le développement et les tests. - -S, --one-site SITE - Ne déployer que le fichier de site spécifié. Cette option implique - --deploy et est utile pour le développement et les tests. --localhosts Créer dans le fichier /etc/hosts tous les noms d'hôte ayant un suffixe .local mentionnés dans les fichiers de site. Cette option est utile pour - le développement et les tests." + le développement et les tests. + -C, --one-conf CONF + Ne déployer que le fichier de configuration spécifié. Cette option est + utilisée avec --deploy et est utile pour le développement et les tests. + -M, --one-module MODULE + Ne déployer que le fichier de module spécifié. Cette option est utilisée + avec --deploy et est utile pour le développement et les tests. + -S, --one-site SITE + Ne déployer que le fichier de site spécifié. Cette option est utilisée + avec --deploy ou --localhosts et est utile pour le développement et les + tests." } # toujours placer une variable dépendante AVANT la variable maitre @@ -177,10 +178,10 @@ args=( -8,--jessie '$array_add TEMPLATECTL_VARS sysver=jessie' -u,--update,--deploy action=deploy -r:,--certsdir: aac_certsdir= - -C:,--one-conf: '$set@ oneconf; action=deploy' - -M:,--one-module: '$set@ onemodule; action=deploy' - -S:,--one-site: '$set@ onesite; action=deploy' --localhosts action=localhosts + -C:,--one-conf: oneconf= + -M:,--one-module: onemodule= + -S:,--one-site: onesite= ) parse_args "$@"; set -- "${args[@]}" @@ -256,18 +257,19 @@ elif [ "$action" == deploy -o "$action" == localhosts ]; then if [ "$action" == deploy ]; then etitle "Mise à jour du système" - args=( - apache_autoconf + args=(apache_autoconf --ignore "$(basename -- "$config")" ${oneconf:+--one-conf "$(basename -- "$oneconf")"} ${onemodule:+--one-module "$(basename -- "$onemodule")"} - ${onesite:+--one-site "$(basename -- "$onesite")"} ) elif [ "$action" == localhosts ]; then etitle "Mise à jour de /etc/hosts" args=(apache_autoconf_localhosts) fi - args=("${args[@]}" "$destdir" "$aac_certsdir") + args=("${args[@]}" + "$destdir" "$aac_certsdir" + ${onesite:+--one-site "$(basename -- "$onesite")"} + ) for __name in "${TEMPLATE_DYNAMIC_VARS[@]}"; do array_add args "$__name=${!__name}" done diff --git a/lib/ulib/apache.tools b/lib/ulib/apache.tools index a10496a..e2f2dfa 100644 --- a/lib/ulib/apache.tools +++ b/lib/ulib/apache.tools @@ -533,9 +533,10 @@ s#@@ca@@#$APACHESSLCERTSDIR/$(basename "$ca")#g function apache_autoconf_localhosts() { eval "$(utools_local)" - local autoconfdir sitesdir + local autoconfdir sitesdir onesite parse_opts "${PRETTYOPTS[@]}" \ --sitesdir: sitesdir= \ + --one-site: onesite= \ @ args -- "$@" && set -- "${args[@]}" || die "$args" __apache_autoconf_setup || return 1 @@ -561,9 +562,12 @@ function apache_autoconf_localhosts() { array_from_lines localips "$( Date: Mon, 7 Dec 2015 12:26:37 +0400 Subject: [PATCH 5/9] ajout de cgilsxml --- lib/ulib/cgilsxml | 8 + lib/ulib/support/cgilsxml.py | 298 +++++++++++++++++++++++++++++++++++ 2 files changed, 306 insertions(+) create mode 100644 lib/ulib/cgilsxml create mode 100755 lib/ulib/support/cgilsxml.py diff --git a/lib/ulib/cgilsxml b/lib/ulib/cgilsxml new file mode 100644 index 0000000..8e78929 --- /dev/null +++ b/lib/ulib/cgilsxml @@ -0,0 +1,8 @@ +##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +## Fonction de support pour cgilsxml +##@cooked nocomments +##@require ulib +uprovide cgilsxml +urequire ulib + +function cgilsxml() { "$ULIBDIR/support/cgilsxml.py" "$@"; } diff --git a/lib/ulib/support/cgilsxml.py b/lib/ulib/support/cgilsxml.py new file mode 100755 index 0000000..b65f76e --- /dev/null +++ b/lib/ulib/support/cgilsxml.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python +# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 + +"""Ce script est prévu pour être utilisé dans un script CGI. +Il permet de lister le contenu d'un répertoire au format XML, et de télécharger les fichiers trouvés. +""" + +import os, sys, re, shutil, mimetypes +from os import path +from time import time, localtime +from types import UnicodeType, StringTypes +import xml.etree.ElementTree as ET + +# _u() et datef() sont des fonctions de pyulib, recopiées ici pour ne pas devoir +# dépendre d'une librairie extérieure +def _u(u): + if type(u) not in StringTypes: u = unicode(str(u), 'utf-8', 'strict') + elif type(u) is not UnicodeType: u = unicode(u, 'utf-8', 'strict') + return u +DATEF_MAP = {'%Y': '%(y)04i', '%m': '%(m)02i', '%d': '%(d)02i', '%H': '%(H)02i', '%M': '%(M)02i', '%S': '%(S)02i'} +DATE_DATEF = '%d/%m/%Y' +TIME_DATEF = '%H:%M:%S' +def datef(format=None, t=None): + if format is None: format = DATE_DATEF + if t is None: t = time() + y, m, d, H, M, S, W, J, dst = localtime(t) + for fr, to in DATEF_MAP.items(): format = format.replace(fr, to) + return format % locals() + +RE_COMMA = re.compile(r'\s*,\s*') +RE_VAR_NAME_EXPR = re.compile(r'([_a-zA-Z][-_a-zA-Z0-9]*)=(.*)') +RE_SORT_FIELD_TYPE = re.compile(r'([_a-zA-Z][-_a-zA-Z0-9]*)(?::([aAdDcCnN]+))?') + +class Filter: + re_spec = None + ex_vars = None + ex_group = None + + def __init__(self, re_spec): + self.re_spec = re.compile(re_spec) + self.ex_vars = [] + self.ex_group = None + + def add_var(self, ex_var): + mo = RE_VAR_NAME_EXPR.match(ex_var) + if mo is None: raise ValueError('Invalid VAR_EXPR: %s' % ex_var) + name, expr = mo.groups() + self.ex_vars.append((name, expr)) + + def set_group(self, ex_group): + self.ex_group = ex_group + + def __repr__(self): + return 'Filter<%s,%s,%s>' % (self.re_spec, repr(self.ex_vars), self.ex_group) + + def match_fill(self, file): + mo = self.re_spec.match(file.name) + if mo is None: return None + for name, expr in self.ex_vars: + value = mo.expand(expr) + if '%' in value: value = value % file.vars + file[name] = value + if self.ex_group is not None: + group = mo.expand(self.ex_group) + if '%' in group: group = group % file.vars + file.group = group + return True + +class File: + pf = None + group = None + vars = None + + def __init__(self, dir, name): + dir = path.abspath(dir) + pf = path.join(dir, name) + self.__dict__['pf'] = pf + self.__dict__['group'] = None + self.__dict__['vars'] = {} + + stat = os.stat(pf) + mtime = int(stat.st_mtime) + + self.dir = dir + self.name = name + self.mtime = mtime + self.date = datef(DATE_DATEF, mtime) + self.time = datef(TIME_DATEF, mtime) + + def isfile(self): + return path.isfile(self.pf) + + def __getitem__(self, name): return self.vars[name] + def __setitem__(self, name, value): self.vars[name] = value + def __getattr__(self, name): return self.vars[name] + def __setattr__(self, name, value): + if self.__dict__.has_key(name): + self.__dict__[name] = value + else: + self.vars[name] = value + def __repr__(self): return 'File<%s>' % (self.pf) + +def find_files(basedir, filters): + files = [] + for name in os.listdir(basedir): + file = File(basedir, name) + if not file.isfile(): continue + matched = False + for filter in filters: + matched = filter.match_fill(file) or matched + if not matched: continue + files.append(file) + return files + +def build_sortfunc(sortby): + SORTS = [] + for term in RE_COMMA.split(sortby.strip()): + mo = RE_SORT_FIELD_TYPE.match(term) + if mo is None: raise ValueError('Invalid SORT_EXPR: %s' % term) + field, type = mo.groups() + if type is None: type = '' + type = type.upper() + if 'A' in type: order = 'A' + elif 'D' in type: order = 'D' + else: order = 'A' + if 'C' in type: method = 'C' + elif 'N' in type: method = 'N' + else: method = 'C' + SORTS.append((field, method, order)) + def sortfunc(a, b): + for field, method, order in SORTS: + av = getattr(a, field) + bv = getattr(b, field) + if method == 'C': + av = str(av) + bv = str(bv) + elif method == 'N': + av = int(av) + bv = int(bv) + if av < bv: outcome = -1 + elif av > bv: outcome = 1 + else: outcome = 0 + if order == 'A': pass + elif order == 'D': outcome = -outcome + if outcome != 0: return outcome + return 0 + return sortfunc + +def sort_files(files, sortfunc): + files.sort(sortfunc) + return files + +def build_fgroups(files): + fgroups = set() + for file in files: + if file.group is not None: + fgroups.add(file.group) + fgroups = list(fgroups) + fgroups.sort() + return fgroups + +def filter_files(files, group): + if group: return filter(lambda file: file.group == group, files) + else: return filter(lambda file: not file.group, files) + +def select_file(files, name): + matches = filter(lambda file: file.name == name, files) + if matches: return matches[0] + else: return None + +def print_files(files, fgroups=None, select_group=None, script_name=None, query_string=None, xslt=None): + xresult = ET.Element("result") + xenv = ET.SubElement(xresult, "env") + if script_name is not None: + ET.SubElement(xenv, "script_name").text = _u(script_name) + if query_string is not None: + ET.SubElement(xenv, "query_string").text = _u(query_string) + xfgroups = ET.SubElement(xresult, "fgroups") + if fgroups is not None: + for fgroup in fgroups: + xfgroup = ET.SubElement(xfgroups, 'fgroup') + xfgroup.text = _u(fgroup) + if fgroup == select_group: + xfgroup.set('selected', 'selected') + xfiles = ET.SubElement(xresult, "files") + for file in files: + xfile = ET.SubElement(xfiles, "file") + if file.group is not None: xfile.set('group', file.group) + for name, value in file.vars.items(): + ET.SubElement(xfile, name).text = _u(value) + + sys.stdout.write('\n') + if xslt is not None: + sys.stdout.write('\n' % xslt) + ET.ElementTree(xresult).write(sys.stdout, "utf-8") + +def run_cgilsxml(): + default_filter = Filter(r'(.*)') + def add_spec(option, opt, value, parser, *args, **kw): + if env['filter'] is not None: env['filters'].append(env['filter']) + env['filter'] = Filter(value) + def add_var(option, opt_str, value, parser, *args, **kw): + if env['filter'] is None: env['filter'] = default_filter + env['filter'].add_var(value) + def set_group(option, opt_str, value, parser, *args, **kw): + if env['filter'] is None: env['filter'] = default_filter + env['filter'].set_group(value) + + from optparse import OptionParser + OP = OptionParser(usage=u"\n\t%prog [options] /path/to/dir", description=__doc__) + OP.add_option('-t', '--xslt', dest='xslt', + help=u"Ajouter le chemin vers la feuille de style XSLT dans le flux XML généré") + OP.add_option('-e', '--spec', dest='spec', action='callback', callback=add_spec, type="string", + help=u"Spécifier l'expression régulière permettant de sélectionner les fichiers à lister. L'expression régulière peut définir des groupes qui sont utilisées pour l'extraction des variables.\nIl est possible de spécifier cette option plusieurs fois.") + OP.add_option('-v', '--var', dest='var', action='callback', callback=add_var, type="string", + help=u"Définir la variable NAME à la valeur de l'expression VAR_EXPR. Dans cette expression, il est possible d'utiliser des expressions de la forme %%(var)s pour inclure des variables déjà définies, ou \\N et \\g pour inclure respective le groupe numéro N et le groupe nommé NAME de l'expression régulière --spec.\nCette option peut être spécifiée plusieurs fois. Elle s'applique à l'expression régulière définie par la dernière option --spec") + OP.add_option('-g', '--group', dest='group', action='callback', callback=set_group, type="string", + help=u"Spécifier l'expression qui permet de construire des ensembles de fichiers sur la base des groupes définis dans l'expression régulière de l'option --spec. Dans cette expression, il est possible d'utiliser des expressions de la forme %%(var)s pour inclure des variables déjà définies, ou \\N ou \\g pour inclure respective le groupe numéro N et le groupe nommé NAME de l'expression régulière --spec.\nCette option ne peut être spécifiée qu'une seule fois par option --spec") + OP.add_option('-s', '--sort', dest='sortby', + help=u"Spécifier le champ sur lequel trier ainsi que le type de tri à utiliser. SORT_EXPR est de la forme FIELD:TYPE où FIELD est le nom du champ et TYPE est le type de tri: A, D, C et/ou N pour (A)scendant, (D)escendant, (C)haine, (N)numérique. Si un champ est spécifié mais que le type de tri ne l'est pas, la valeur par défaut est AC. Si cette option n'est pas spécifiée, le tri par défaut est 'mtime:DN'.\nIl est possible de spécifier plusieurs champs pour le tri en les séparant par des virgules.") + OP.add_option('--cgi', dest='cgi_mode', action='store_true', + help=u"Activer le mode CGI. Ce mode est automatiquement activé si la variable d'environnement REQUEST_METHOD existe.") + OP.add_option('--cgi-path-info', dest='path_info', + help=u"Spécifier un chemin d'un fichier à télécharger. En mode CGI, cette valeur est prise dans la variable d'environnement PATH_INFO") + OP.add_option('--cgi-script-name', dest='script_name', + help=u"Spécifier la valeur de SCRIPT_NAME pour provisionner l'environnement du fichier résultat. En mode CGI, cette valeur est prise dans la variable d'environnement du même nom.") + OP.add_option('--cgi-query-string', dest='query_string', + help=u"Spécifier la valeur de QUERY_STRING pour provisionner l'environnement du fichier résultat. En mode CGI, cette valeur est prise dans la variable d'environnement du même nom.") + OP.add_option('--cgi-param-group', dest='select_group', + help=u"Sélectionner le groupe spécifié. Seuls les fichiers du groupe sont affichés. En mode CGI, cette option est automatiquement activée si QUERY_STRING contient le paramètre group=GROUP.\nNote: pour le calcul de cette valeur, l'API cgi.FieldStorage() est utilisé, ce qui fait que la valeur éventuellement fournie par l'option --cgi-query-string est ignorée.") + env = dict(filters=[], filter=None) + o, args = OP.parse_args() + filters = env['filters'] + filter = env['filter'] + xslt, sortby, cgi_mode = o.xslt, o.sortby, o.cgi_mode + cgi_path_info = o.path_info + cgi_script_name = o.script_name + cgi_query_string = o.query_string + cgi_select_group = o.select_group + + environ = os.environ + if cgi_mode is None: cgi_mode = 'REQUEST_METHOD' in environ + if cgi_mode: + import cgi + form = cgi.FieldStorage() + if 'group' in form and cgi_select_group is None: + cgi_select_group = form.getfirst("group") + if cgi_path_info is None and 'PATH_INFO' in environ : + cgi_path_info = environ.get('PATH_INFO') + if cgi_script_name is None and 'SCRIPT_NAME' in environ : + cgi_script_name = environ.get('SCRIPT_NAME') + if cgi_query_string is None and 'QUERY_STRING' in environ : + cgi_query_string = environ.get('QUERY_STRING') + if cgi_path_info is not None: cgi_path_info = path.split(cgi_path_info)[1] + + if filter is not None: filters.append(filter) + if filter is None and not filters: filters.append(default_filter) + if len(args) == 0: args = ['.'] + if sortby is None: sortby = 'mtime:DN' + + files = [] + for basedir in args: + files.extend(find_files(basedir, filters)) + sortfunc = build_sortfunc(sortby) + files = sort_files(files, sortfunc) + fgroups = build_fgroups(files) + if cgi_mode and fgroups and cgi_select_group is None: + # En mode CGI, s'il y a plusieurs groupes, ne pas afficher la liste + # complète, mais requérir la sélection d'un groupe + cgi_select_group = '' + if cgi_select_group is not None: + files = filter_files(files, cgi_select_group) + if cgi_path_info: + file = select_file(files, cgi_path_info) + if file is None: + if cgi_mode: + print "Content-Type: text/plain; charset=UTF-8" + print + print "Impossible de trouver le fichier %s" % cgi_path_info + else: + if cgi_mode: + ctype, encoding = mimetypes.guess_type(file.name, False) + if ctype is None: ctype = "application/octet-stream" + print "Content-Type: %s; charset=utf-8" % ctype + print "Content-Disposition: attachment; filename=\"%s\"" % file.name + print + inf = open(file.pf, 'rb') + try: shutil.copyfileobj(inf, sys.stdout) + finally: inf.close() + sys.exit() + + if cgi_mode: + print "Content-Type: text/xml; charset=UTF-8" + print + print_files(files, fgroups, cgi_select_group, cgi_script_name, cgi_query_string, xslt) + +if __name__ == '__main__': + run_cgilsxml() From eac94a7f323ba4c71ebc11172963038f685c101c Mon Sep 17 00:00:00 2001 From: Jephte CLAIN Date: Mon, 7 Dec 2015 13:10:40 +0400 Subject: [PATCH 6/9] =?UTF-8?q?cgiupload:=20possibilit=C3=A9=20de=20filtre?= =?UTF-8?q?r=20sur=20le=20nom=20du=20fichier=20ou=20son=20type=20de=20cont?= =?UTF-8?q?enu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/ulib/support/cgiupload.py | 38 +++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/lib/ulib/support/cgiupload.py b/lib/ulib/support/cgiupload.py index cdfd718..59b29e2 100755 --- a/lib/ulib/support/cgiupload.py +++ b/lib/ulib/support/cgiupload.py @@ -3,30 +3,49 @@ u"""Ce script est prévu pour être utilisé dans un script CGI. Il enregistre les fichiers uploadés dans le répertoire spécifié, et éventuellement lance une commande avec comme argument les fichiers enregistrés. + +Retourner 0 si tous les fichiers ont été traités, 1 si au moins un fichier a été rejeté, 2 si aucun fichier ne correspondait (ou si aucun fichier n'a été fourni) """ -import sys, os, types, cgi, tempfile +import sys, os, re, types, cgi, tempfile from os import path cgitb = None # marqueur pour savoir si le module cgitb a été importé #import cgitb; cgitb.enable() class FileManager(object): - def __init__(self, destdir=None, parse_env=True): + fileinfos = None + tries = None + count = None + destdir = None + spec = None + type = None + exitcode = None + + def __init__(self, destdir=None, spec=None, type=None, parse_env=True): if destdir is None: destdir = '/tmp' self.fileinfos = [] + self.tries = 0 self.count = 0 self.destdir = destdir + if spec is not None: spec = re.compile(spec) + self.spec = spec + self.type = type if parse_env: self.parse_env() def parse_env(self): class FieldStorage(cgi.FieldStorage): def make_file(self, binary=None): - return self.fm._new_file(self) + outf = self.fm._new_file(self) + if outf is not None: return outf + return cgi.FieldStorage.make_file(self, binary) FieldStorage.fm = self self.form = FieldStorage() def _new_file(self, form): + self.tries += 1 + if self.spec is not None and self.spec.match(form.filename) is None: return None + if self.type is not None and self.type != form.type: return None fp = path.join(self.destdir, 'upload%i' % self.count) outf = open(fp, 'w+b') self.count += 1 @@ -45,11 +64,20 @@ class FileManager(object): finally: if close: outf.close() + def exitcode(self): + if self.count == 0: return 2 + elif self.count != self.tries: return 1 + else: return 0 + # Analyser les options from optparse import OptionParser parser = OptionParser(usage=u"\n\t%prog [options]\n\t%prog [options] -c command [args]", description=__doc__) parser.add_option('-d', '--destdir', dest='destdir', help=u"Spécifier le répertoire de destination des fichiers uploadés. Ce répertoire est créé si nécessaire.") +parser.add_option('-e', '--spec', dest='spec', + help=u"N'accepter que des fichiers dont le nom correspond à l'expression régulière spécifiée") +parser.add_option('-t', '--type', dest='type', + help=u"N'accepter que des fichiers dont le type mime correspond à la valeur spécifiée") parser.add_option('-p', '--printcsv', action='store_true', dest='printcsv', help=u"Afficher la liste des fichiers uploadés au format CSV. Chaque ligne est de la forme 'name,value,file,type' où name est le nom du champ dans le formulaire http, value le nom du fichier tel qu'il a été fourni par le client, file le chemin complet vers le fichier uploadé, et type le type mime du contenu.") parser.add_option('-c', '--cmd', action='store_true', dest='cmd', @@ -84,7 +112,7 @@ if o.printcsv is None and o.cmd is None: if o.cmd and o.clear is None: o.clear = True -fm = FileManager(o.destdir) +fm = FileManager(o.destdir, o.spec, o.type) if o.printcsv: fm.write_csv(sys.stdout) @@ -112,3 +140,5 @@ if o.clear: for fi in fm.fileinfos: try: os.remove(fi[0]) except: pass + +sys.exit(fm.exitcode()) From d7b468d9fd77b8feaf11d1545b01f8892312d38d Mon Sep 17 00:00:00 2001 From: Jephte CLAIN Date: Mon, 7 Dec 2015 14:59:20 +0400 Subject: [PATCH 7/9] cgilsxml: ajouter les variables dlname, size, Y, m, d, H, M, S. maj doc --- lib/ulib/support/cgilsxml.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/ulib/support/cgilsxml.py b/lib/ulib/support/cgilsxml.py index b65f76e..486f59b 100755 --- a/lib/ulib/support/cgilsxml.py +++ b/lib/ulib/support/cgilsxml.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 -"""Ce script est prévu pour être utilisé dans un script CGI. +u"""Ce script est prévu pour être utilisé dans un script CGI. + Il permet de lister le contenu d'un répertoire au format XML, et de télécharger les fichiers trouvés. """ @@ -83,15 +84,19 @@ class File: self.dir = dir self.name = name + self.dlname = name + self.size = stat.st_size self.mtime = mtime self.date = datef(DATE_DATEF, mtime) self.time = datef(TIME_DATEF, mtime) + self.Y, self.m, self.d, self.H, self.M, self.S = datef('%Y %m %d %H %M %S', mtime).split(' ') def isfile(self): return path.isfile(self.pf) def __getitem__(self, name): return self.vars[name] def __setitem__(self, name, value): self.vars[name] = value + def has_key(self, name): return self.vars.has_key(name) def __getattr__(self, name): return self.vars[name] def __setattr__(self, name, value): if self.__dict__.has_key(name): @@ -211,7 +216,7 @@ def run_cgilsxml(): OP.add_option('-t', '--xslt', dest='xslt', help=u"Ajouter le chemin vers la feuille de style XSLT dans le flux XML généré") OP.add_option('-e', '--spec', dest='spec', action='callback', callback=add_spec, type="string", - help=u"Spécifier l'expression régulière permettant de sélectionner les fichiers à lister. L'expression régulière peut définir des groupes qui sont utilisées pour l'extraction des variables.\nIl est possible de spécifier cette option plusieurs fois.") + help=u"Spécifier l'expression régulière permettant de sélectionner les fichiers à lister. L'expression régulière peut définir des groupes qui sont utilisées pour l'extraction des variables.\nIl est possible de spécifier cette option plusieurs fois.\nNote: TOUTES les expressions régulières sont testées par rapport au nom du fichier, et pour celles qui correspondent, les variables correspondantes sont définies. Il faut donc ordonner les expressions régulières de la plus générale à la plus spécifique, contrairement à ce qui se fait d'habitude.") OP.add_option('-v', '--var', dest='var', action='callback', callback=add_var, type="string", help=u"Définir la variable NAME à la valeur de l'expression VAR_EXPR. Dans cette expression, il est possible d'utiliser des expressions de la forme %%(var)s pour inclure des variables déjà définies, ou \\N et \\g pour inclure respective le groupe numéro N et le groupe nommé NAME de l'expression régulière --spec.\nCette option peut être spécifiée plusieurs fois. Elle s'applique à l'expression régulière définie par la dernière option --spec") OP.add_option('-g', '--group', dest='group', action='callback', callback=set_group, type="string", @@ -279,10 +284,12 @@ def run_cgilsxml(): print "Impossible de trouver le fichier %s" % cgi_path_info else: if cgi_mode: - ctype, encoding = mimetypes.guess_type(file.name, False) + dlname = file.dlname or file.name + ctype = file.has_key('type') and file.type or None + if ctype is None: ctype, encoding = mimetypes.guess_type(dlname, False) if ctype is None: ctype = "application/octet-stream" print "Content-Type: %s; charset=utf-8" % ctype - print "Content-Disposition: attachment; filename=\"%s\"" % file.name + print "Content-Disposition: attachment; filename=\"%s\"" % dlname print inf = open(file.pf, 'rb') try: shutil.copyfileobj(inf, sys.stdout) From 22b8ddbf9e782f09eae5a9350f68245b6e64c150 Mon Sep 17 00:00:00 2001 From: Jephte CLAIN Date: Mon, 7 Dec 2015 15:12:32 +0400 Subject: [PATCH 8/9] Init changelog & version 2.21.0 --- CHANGES.txt | 10 ++++++++++ VERSION.txt | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3ce2864..77e5e8b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,13 @@ +## Version 2.21.0 du 07/12/2015-15:12 + +d7b468d cgilsxml: ajouter les variables dlname, size, Y, m, d, H, M, S. maj doc +eac94a7 cgiupload: possibilité de filtrer sur le nom du fichier ou son type de contenu +fae2111 ajout de cgilsxml +26fe709 apacheconfig: -S peut aussi être utilisé avec --localhosts +7c6ad6a apacheconfig: nouvelles options pour le développement +c2d5eab pxs fait aussi git pull avec un dépôt sans annexe +dca7bee pxs fonctionne pour les dépôt normaux comme pci -A + ## Version 2.20.0 du 12/11/2015-15:41 16d95d2 apache_autoconf(): déploiement des certificats autorité diff --git a/VERSION.txt b/VERSION.txt index 7329e21..db65e21 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -2.20.0 +2.21.0 From a7c8e21cc03c8415315d8d70ebdb1b13331b87be Mon Sep 17 00:00:00 2001 From: Jephte CLAIN Date: Mon, 7 Dec 2015 15:12:48 +0400 Subject: [PATCH 9/9] maj ulib --- lib/ulib/.ulibver | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ulib/.ulibver b/lib/ulib/.ulibver index 8dfc05e..ad99c9a 100644 --- a/lib/ulib/.ulibver +++ b/lib/ulib/.ulibver @@ -1 +1 @@ -009007000 +009008000