diff --git a/CHANGES.txt b/CHANGES.txt index 08bd3a3..95f9435 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,7 @@ +## Version 3.2.0 du 07/02/2016-23:05 + +24f3211 cgiparams.py et cgilsxml.py: modifications pour gérer la construction de QUERY_STRING + ## Version 3.1.0 du 04/02/2016-13:52 3190a32 cgilsxml.py: ajout de la variable script_base, qui permet de faire des urls relatives diff --git a/VERSION.txt b/VERSION.txt index fd2a018..944880f 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -3.1.0 +3.2.0 diff --git a/lib/ulib/.ulibver b/lib/ulib/.ulibver index d125e4d..e0af680 100644 --- a/lib/ulib/.ulibver +++ b/lib/ulib/.ulibver @@ -1 +1 @@ -010001000 +011000000 diff --git a/lib/ulib/support/cgilsxml.py b/lib/ulib/support/cgilsxml.py index 6899bec..294681f 100755 --- a/lib/ulib/support/cgilsxml.py +++ b/lib/ulib/support/cgilsxml.py @@ -6,7 +6,7 @@ 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. """ -import os, sys, re, shutil, mimetypes, urlparse +import os, sys, re, shutil, mimetypes, urlparse, urllib from os import path from time import time, localtime from types import UnicodeType, StringTypes @@ -178,18 +178,58 @@ def cgi_nocache(): print "Pragma: no-cache" print "Expires: Thu, 01 Jan 1970 00:00:00 GMT" -def print_files(files, fgroups=None, select_group=None, script_name=None, query_string=None, xslt=None): +RE_COMMA = re.compile(',') +def lfix(values): + if values is None: return None + fvalues = [] + for parts in values: + parts = RE_COMMA.split(parts) + fvalues.extend(parts) + return fvalues + +def filter_query_string(query_string, includes=None, excludes=None, prefix=None): + params = urlparse.parse_qsl(query_string, keep_blank_values=True) + includes = lfix(includes) + if includes is None: names = dict(params).keys() + else: names = includes[:] + excludes = lfix(excludes) + if excludes is not None: + for name in excludes: + if name in names: names.remove(name) + params = [(name, value) for (name, value) in params if name in names] + query_string = urllib.urlencode(params) + if prefix: + if query_string != "": query_string = "%s&%s" % (prefix, query_string) + elif prefix != "": query_string = prefix + return query_string + +def print_files(files, fgroups=None, select_group=None, script_name=None, xslt=None, + query_string=None, includes=None, excludes=None, prefix=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) ET.SubElement(xenv, "script_base").text = _u(re.sub(r'[^/]+$', '', script_name)) if query_string is not None: + fquery_string = filter_query_string(query_string, includes, excludes, prefix) query_string = _u(query_string) + params = urlparse.parse_qsl(query_string, keep_blank_values=True) + fquery_string = _u(fquery_string) + fparams = urlparse.parse_qsl(fquery_string, keep_blank_values=True) + if includes or excludes or prefix: + xorig = ET.SubElement(xenv, "orig") + if query_string: query_string = u'?%s' % query_string + ET.SubElement(xorig, "query_string").text = query_string + xvars = ET.SubElement(xorig, "query_vars") + for name, value in params: + ET.SubElement(xvars, name).text = value + query_string = fquery_string + params = fparams + if query_string: query_string = u'?%s' % query_string ET.SubElement(xenv, "query_string").text = query_string xvars = ET.SubElement(xenv, "query_vars") - for name, value in urlparse.parse_qsl(query_string, keep_blank_values=True): - ET.SubElement(xvars, "var", name=name, value=value) + for name, value in params: + ET.SubElement(xvars, name).text = value xfgroups = ET.SubElement(xresult, "fgroups") if fgroups is not None: for fgroup in fgroups: @@ -246,17 +286,20 @@ def run_cgilsxml(): 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 QUERY_STRING.") OP.add_option('-N', '--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 option est automatiquement activée si QUERY_STRING contient le paramètre script_name=SCRIPT_NAME, la valeur par défaut étant la valeur de la variable d'environnement SCRIPT_NAME." - + u"\n Note: pour la lecture des paramètres de QUERY_STRING, l'API cgi.FieldStorage() est utilisé, ce qui fait que la valeur éventuellement fournie par l'option --cgi-query-string est ignorée." + u"\n L'ordre de priorité pour le calcul de cette valeur est: d'abord le paramètre script_name dans QUERY_STRING, puis l'option de la ligne de commande, enfin la valeur de la variable d'environnement") OP.add_option('-G', '--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." + u"\n S'il n'y a qu'un seul groupe, il est automatiquement sélectionné." - + u"\n cf la note pour --cgi-script-name pour le calcul de cette valeur." + u"\n L'ordre de priorité pour le calcul de cette valeur est: d'abord le paramètre group dans QUERY_STRING, puis l'option de la ligne de commande") OP.add_option('-t', '--cgi-param-xslt', dest='xslt', help=u"Ajouter le chemin vers la feuille de style XSLT dans le flux XML généré. En mode CGI, cette option est automatiquement activée si QUERY_STRING contient le paramètre xslt=XSLT." - + u"\n cf la note pour --cgi-script-name pour le calcul de cette valeur." + u"\n L'ordre de priorité pour le calcul de cette valeur est: d'abord le paramètre xslt dans QUERY_STRING, puis l'option de la ligne de commande") + OP.add_option('-i', '--include', dest='includes', action='append', + help=u"Spécifier un paramètre à inclure pour construire la valeur du chemin xpath /result/env/query_string dans le résultat. Il est possible de spécifier plusieurs paramètres en les séparant par des virgules. Par défaut, prendre tous les paramètres de la requête.") + OP.add_option('-x', '--exclude', dest='excludes', action='append', + help=u"Spécifier un paramètre à exclure pour construire la valeur du chemin xpath /result/env/query_string dans le résultat. Il est possible de spécifier plusieurs paramètres en les séparant par des virgules.") + OP.add_option('-p', '--prefix', dest="prefix", + help=u"Ajouter les paramètres supplémentaires spécifiés à /result/env/query_string.") env = dict(filters=[], filter=None) o, args = OP.parse_args() filters = env['filters'] @@ -266,14 +309,15 @@ def run_cgilsxml(): environ = os.environ cgi_cache = o.cgi_allow_cache and True or False cgi_mode = o.cgi_mode + cgi_query_string = o.query_string + if cgi_query_string is not None: environ['QUERY_STRING'] = cgi_query_string if cgi_mode is None: cgi_mode = 'REQUEST_METHOD' in environ if cgi_mode: import cgi; form = cgi.FieldStorage() cgi_path_info = o.path_info if cgi_path_info is None and 'PATH_INFO' in environ : cgi_path_info = environ.get('PATH_INFO') - cgi_query_string = o.query_string - if cgi_query_string is None and 'QUERY_STRING' in environ : cgi_query_string = environ.get('QUERY_STRING') + if cgi_query_string is None and 'QUERY_STRING' in environ: cgi_query_string = environ.get('QUERY_STRING') cgi_script_name = None if 'script_name' in form and cgi_script_name is None: cgi_script_name = form.getfirst("script_name") if cgi_script_name is None: cgi_script_name = o.script_name @@ -339,7 +383,8 @@ def run_cgilsxml(): if not cgi_cache: cgi_nocache() print "Content-Type: text/xml; charset=UTF-8" print - print_files(files, fgroups, cgi_select_group, cgi_script_name, cgi_query_string, cgi_xslt) + print_files(files, fgroups, cgi_select_group, cgi_script_name, cgi_xslt, + cgi_query_string, o.includes, o.excludes, o.prefix) if __name__ == '__main__': run_cgilsxml() diff --git a/lib/ulib/support/cgiparams.py b/lib/ulib/support/cgiparams.py index 1087b89..da1d7db 100755 --- a/lib/ulib/support/cgiparams.py +++ b/lib/ulib/support/cgiparams.py @@ -3,10 +3,16 @@ u"""Ce script est prévu pour être utilisé dans un script CGI. -Il permet de lister les paramètres du formulaire et d'y accéder depuis un script bash +Il permet de lister les paramètres du formulaire et d'y accéder depuis un script +bash. Le tableau QVARS est initialisé avec la liste des variables correspondant +aux paramètres pour lesquels une valeur est définie. + +Les arguments de ce script doivent être de la forme NAME[=DEFAULT]. Si le +paramètres était fourni dans la requête, il est affiché, sous forme de scalaire +ou de tableau. S'il n'était pas fourni, la valeur par défaut est affichée. """ -import re, cgi +import re, cgi, urllib RE_NAME_VALUE = re.compile(r'([a-zA-Z0-9_-]+)(?:=(.*))?$') @@ -21,17 +27,58 @@ def print_scalar(name, value): print "%s=%s" % (name, quote(value)) def print_array(name, values): - print "%s=(" % name + print "%s=(" % name, for value in values: - print quote(value, True) + print quote(value, True), print ")" +RE_COMMA = re.compile(',') +def lfix(values): + if values is None: return None + fvalues = [] + for parts in values: + parts = RE_COMMA.split(parts) + fvalues.extend(parts) + return fvalues + +def build_query_string(form, includes=None, excludes=None, prefix=None): + includes = lfix(includes) + if includes is None: names = form.keys() + else: names = includes[:] + excludes = lfix(excludes) + if excludes is not None: + for name in excludes: + if name in names: names.remove(name) + params = [] + for name in names: + values = form.getlist(name) + for value in values: + params.append((name, value)) + query_string = urllib.urlencode(params) + if prefix: + if query_string != "": query_string = "%s&%s" % (prefix, query_string) + elif prefix != "": query_string = prefix + if query_string: + query_string = "?%s" % query_string + return query_string + def run_cgiparams(): from optparse import OptionParser OP = OptionParser(usage=u"\n\t%prog [options] params...", description=__doc__) + OP.add_option('--qvars', dest="qvars", default="QVARS", + help=u"Spécifier le nom du tableau qui contiendra la liste des variables correspondant aux paramètres pour lesquels une valeur est définie. Par défaut, utiliser QVARS") + OP.add_option('-q', '--query-string', action='store_true', dest='print_qs', + help=u"Reconstruire et afficher la valeur de QUERY_STRING en fonction des paramètres fournis dans la requête. Les options -i et -x permettent de sélectionner les paramètres qui y sont inclus.") + OP.add_option('-i', '--include', dest='includes', action='append', + help=u"Spécifier un paramètre à inclure pour construire QUERY_STRING. Il est possible de spécifier plusieurs paramètres en les séparant par des virgules.") + OP.add_option('-x', '--exclude', dest='excludes', action='append', + help=u"Spécifier un paramètre à exclure pour construire QUERY_STRING. Il est possible de spécifier plusieurs paramètres en les séparant par des virgules.") + OP.add_option('-p', '--prefix', dest="prefix", + help=u"Ajouter les paramètres supplémentaires spécifiés à QUERY_STRING.") o, args = OP.parse_args() form = cgi.FieldStorage(keep_blank_values=True) + qvars = [] if args: for nv in args: mo = RE_NAME_VALUE.match(nv) @@ -39,6 +86,7 @@ def run_cgiparams(): fname, defvalue = mo.group(1), mo.group(2) if defvalue is None: defvalue = '' vname = fname.replace('-', '_') + if vname not in qvars: qvars.append(vname) values = form.getlist(fname) if len(values) == 0: print_scalar(vname, defvalue) elif len(values) == 1: print_scalar(vname, values[0]) @@ -46,9 +94,14 @@ def run_cgiparams(): else: for fname in form.keys(): vname = fname.replace('-', '_') + qvars.append(vname) values = form.getlist(fname) if len(values) == 1: print_scalar(vname, values[0]) else: print_array(vname, values) + print_array(o.qvars, qvars) + if o.print_qs: + qs = build_query_string(form, o.includes, o.excludes, o.prefix) + print_scalar('QUERY_STRING', qs) if __name__ == '__main__': run_cgiparams()