nutools/lib/ulib/support/cgilsxml.py

655 lines
32 KiB
Python
Raw Normal View History

2021-02-24 10:55:56 +04:00
#!/usr/bin/env python2
2015-12-07 12:26:37 +04:00
# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
u"""Ce script est prévu pour être utilisé dans un script CGI.
2015-12-07 12:26:37 +04:00
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, urllib, fnmatch, glob
2015-12-07 12:26:37 +04:00
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 u is None: return None
elif type(u) not in StringTypes: u = unicode(str(u), 'utf-8', 'strict')
2015-12-07 12:26:37 +04:00
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
glob = None
caret_anchor = None
deep_scan = None
re_allows = None
2015-12-07 12:26:37 +04:00
ex_vars = None
ex_group = None
ex_break_on = None
force_disposition = None
force_type = None
force_charset = None
2015-12-07 12:26:37 +04:00
def __init__(self, spec=None, glob=None):
if spec is not None:
self.re_spec = re.compile(spec)
self.caret_anchor = spec.startswith('^')
self.deep_scan = '/' in spec
elif glob is not None:
self.glob = glob
self.deep_scan = '/' in glob
else: raise ValueError("either spec or glob must be specified")
self.re_allows = []
2015-12-07 12:26:37 +04:00
self.ex_vars = []
self.ex_group = None
self.ex_break_on = None
2015-12-07 12:26:37 +04:00
def allow_spec(self, re_allow):
self.re_allows.append(re.compile(re_allow))
2015-12-07 12:26:37 +04:00
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 set_break_on(self, ex_break_on):
self.ex_break_on = ex_break_on
2015-12-07 12:26:37 +04:00
def __repr__(self):
return 'Filter<%s||%s,%s,%s,%s,%s>' % \
(self.re_spec, self.glob, repr(self.re_allows),
repr(self.ex_vars),
self.ex_group, self.ex_break_on,
)
2015-12-07 12:26:37 +04:00
def match_fill(self, file, basedir=None):
if self.glob:
if self.deep_scan:
# la correspondance a déjà été faite avant avec glob.glob(). on
# se contente d'initialiser l'objet
pass
elif not fnmatch.fnmatch(file.name, self.glob):
return None
file.group = None
file.break_on = None
if self.force_disposition: file.content_disposition = self.force_disposition
if self.force_type: file.content_type = self.force_type
if self.force_charset: file.content_charset = self.force_charset
return True
if self.deep_scan:
pf = file.pf
if not self.caret_anchor:
blen = len(basedir)
if pf[:blen] != basedir: return None
pf = pf[blen:]
if not pf.startswith('/'): return None
pf = pf[1:]
mo = self.re_spec.match(pf)
else: mo = self.re_spec.match(file.name)
2015-12-07 12:26:37 +04:00
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
else:
group = None
file.group = group
if self.ex_break_on is not None:
break_on = mo.expand(self.ex_break_on)
if '%' in break_on: break_on = break_on % file.vars
else:
break_on = None
file.break_on = break_on
if self.force_disposition: file.content_disposition = self.force_disposition
if self.force_type: file.content_type = self.force_type
if self.force_charset: file.content_charset = self.force_charset
2015-12-07 12:26:37 +04:00
return True
def match_allow(self, file):
for re_allow in self.re_allows:
if re_allow.match(file.name) is not None:
return True
return False
2015-12-07 12:26:37 +04:00
class File:
pf = None
group = None
vars = None
dontlist = None
2015-12-07 12:26:37 +04:00
def __init__(self, dir=None, name=None, pf=None, dontlist=False):
if pf is None:
if dir is None or name is None: raise ValueError("either (dir,name) or pf must be specified")
pf = path.join(dir, name)
dir, name = path.split(pf) # il est possible que name soit de la forme subdir/name
2015-12-07 12:26:37 +04:00
dir = path.abspath(dir)
pf = path.join(dir, name)
self.__dict__['pf'] = pf
self.__dict__['group'] = None
self.__dict__['vars'] = {}
self.__dict__['dontlist'] = dontlist
2015-12-07 12:26:37 +04:00
stat = os.stat(pf)
mtime = int(stat.st_mtime)
self.dir = dir
self.name = name
self.dlname = name
self.size = stat.st_size
2015-12-07 12:26:37 +04:00
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(' ')
2015-12-07 12:26:37 +04:00
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):
try: return self.vars[name]
except KeyError: raise AttributeError(name)
2015-12-07 12:26:37 +04:00
def __setattr__(self, name, value):
if self.__dict__.has_key(name): self.__dict__[name] = value
else: self.vars[name] = value
2015-12-07 12:26:37 +04:00
def __repr__(self): return 'File<%s>' % (self.pf)
def find_files(basedir, filters, spec_method='all'):
basedir = path.abspath(basedir)
2015-12-07 12:26:37 +04:00
files = []
2017-04-06 22:23:37 +04:00
deep_filters = [filter for filter in filters if filter.re_spec is not None and filter.deep_scan]
if deep_filters:
for dirpath, dirnames, filenames in os.walk(basedir):
for filename in filenames:
# race condition: si le fichier disparait entre-temps, une
# exception se produit
try: file = File(dirpath, filename)
except: continue
matched1 = matched = False
2017-04-06 22:23:37 +04:00
allowed = False
for filter in deep_filters:
if spec_method == 'all' or \
(spec_method == 'first' and not matched):
matched1 = filter.match_fill(file, basedir)
else:
matched1 = False
matched = matched1 or matched
2017-04-06 22:23:37 +04:00
allowed = filter.match_allow(file) or allowed
if matched: pass
elif allowed: file.dontlist = True
else: continue
files.append(file)
2017-04-06 22:23:37 +04:00
simple_filters = [filter for filter in filters if filter.re_spec is not None and not filter.deep_scan]
if simple_filters:
for name in os.listdir(basedir):
# race condition: si le fichier disparait entre-temps, une
# exception se produit
try: file = File(basedir, name)
except: continue
if not file.isfile(): continue
matched1 = matched = False
2017-04-06 22:23:37 +04:00
allowed = False
for filter in simple_filters:
if spec_method == 'all' or \
(spec_method == 'first' and not matched):
matched1 = filter.match_fill(file, basedir)
else:
matched1 = False
matched = matched1 or matched
2017-04-06 22:23:37 +04:00
allowed = filter.match_allow(file) or allowed
if matched: pass
elif allowed: file.dontlist = True
else: continue
files.append(file)
2017-04-06 22:23:37 +04:00
glob_filters = [filter for filter in filters if filter.glob is not None]
if glob_filters:
for filter in glob_filters:
candidates = []
if filter.deep_scan:
for pf in glob.glob(path.join(basedir, filter.glob)):
# race condition: si le fichier disparait entre-temps, une
# exception se produit
try: file = File(pf=pf)
except: continue
candidates.append(file)
else:
for name in os.listdir(basedir):
# race condition: si le fichier disparait entre-temps, une
# exception se produit
try: file = File(basedir, name)
except: continue
candidates.append(file)
for file in candidates:
if not file.isfile(): continue
elif filter.match_fill(file): pass
elif filter.match_allow(file): file.dontlist = True
else: continue
files.append(file)
2017-04-06 22:23:37 +04:00
2015-12-07 12:26:37 +04:00
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, None)
bv = getattr(b, field, None)
if av is None:
if bv is None: outcome = 0
else: outcome = 1
elif bv is None:
outcome = -1
else:
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
2015-12-07 12:26:37 +04:00
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: func = lambda file: file.dontlist or file.group == group
else: func = lambda file: file.dontlist or not file.group
return filter(func, files)
2015-12-07 12:26:37 +04:00
def select_file(files, name):
matches = filter(lambda file: file.name == name, files)
if matches: return matches[0]
else: return None
def cgi_nocache():
print "Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0"
print "Pragma: no-cache"
print "Expires: Thu, 01 Jan 1970 00:00:00 GMT"
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):
2015-12-07 12:26:37 +04:00
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))
2016-06-09 16:29:24 +04:00
orig_query_string = query_string
if orig_query_string is not None:
query_string = orig_query_string
params = []
if query_string is not None:
params = urlparse.parse_qsl(query_string, keep_blank_values=True)
query_string = _u(query_string)
fquery_string = orig_query_string
fparams = []
if fquery_string is not None:
fquery_string = filter_query_string(fquery_string, includes, excludes, prefix)
fparams = urlparse.parse_qsl(fquery_string, keep_blank_values=True)
fquery_string = _u(fquery_string)
2016-06-09 16:29:24 +04:00
if includes or excludes or prefix:
xorig = ET.SubElement(xenv, "orig")
if query_string:
ET.SubElement(xorig, "query_string").text = u'?%s' % query_string
ET.SubElement(xorig, "squery_string").text = u'&%s' % 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
2016-06-09 16:29:24 +04:00
if query_string:
ET.SubElement(xenv, "query_string").text = u'?%s' % query_string
ET.SubElement(xenv, "squery_string").text = u'&%s' % query_string
2016-06-09 16:29:24 +04:00
xvars = ET.SubElement(xenv, "query_vars")
for name, value in params:
ET.SubElement(xvars, name).text = value
2016-06-09 16:29:24 +04:00
2015-12-07 12:26:37 +04:00
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')
2016-06-09 16:29:24 +04:00
2015-12-07 12:26:37 +04:00
xfiles = ET.SubElement(xresult, "files")
first = True
last_classifier = (None, None)
2015-12-07 12:26:37 +04:00
for file in files:
if file.dontlist: continue
current_classifier = (file.group, file.break_on)
if first or current_classifier != last_classifier:
file.break_here = None
last_classifier = current_classifier
first = False
2015-12-07 12:26:37 +04:00
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('<?xml version="1.0" encoding="utf-8"?>\n')
2015-12-07 12:26:37 +04:00
if xslt is not None:
sys.stdout.write('<?xml-stylesheet version="1.0" type="text/xsl" href="%s"?>\n' % xslt)
ET.ElementTree(xresult).write(sys.stdout, "utf-8")
def run_cgilsxml():
class ContentInfos:
disposition = 'attachment'
type = None
charset = 'utf-8'
default_content = ContentInfos()
2015-12-07 12:26:37 +04:00
default_filter = Filter(r'(.*)')
env = dict(filters=[], filter=None)
2015-12-07 12:26:37 +04:00
def add_spec(option, opt, value, parser, *args, **kw):
if env['filter'] is not None: env['filters'].append(env['filter'])
env['filter'] = Filter(spec=value)
def add_glob(option, opt, value, parser, *args, **kw):
if env['filter'] is not None: env['filters'].append(env['filter'])
env['filter'] = Filter(glob=value)
def allow_spec(option, opt, value, parser, *args, **kw):
if env['filter'] is None: env['filter'] = default_filter
env['filter'].allow_spec(value)
def add_var(option, opt, value, parser, *args, **kw):
2015-12-07 12:26:37 +04:00
if env['filter'] is None: env['filter'] = default_filter
env['filter'].add_var(value)
def set_group(option, opt, value, parser, *args, **kw):
2015-12-07 12:26:37 +04:00
if env['filter'] is None: env['filter'] = default_filter
env['filter'].set_group(value)
def set_break_on(option, opt, value, parser, *args, **kw):
if env['filter'] is None: env['filter'] = default_filter
env['filter'].set_break_on(value)
def set_content_disposition(option, opt, value, parser, *args, **kw):
if value in ('attachment', 'inline'): pass
elif value == 'none': value = None
else:
if cgi_mode:
if not cgi_cache: cgi_nocache()
print "Content-Type: text/plain; charset=utf-8"
print
print "%s: Disposition de contenu invalide. Ce soit être attachment, inline ou none" % cgi_content_disposition
sys.exit(1)
if env['filter'] is None: default_content.disposition = value
else: env['filter'].force_disposition = value
def set_content_type(option, opt, value, parser, *args, **kw):
if env['filter'] is None: default_content.type = value
else: env['filter'].force_type = value
def set_content_charset(option, opt, value, parser, *args, **kw):
if env['filter'] is None: default_content.charset = value
else: env['filter'].force_charset = value
2015-12-07 12:26:37 +04:00
from optparse import OptionParser
OP = OptionParser(usage=u"\n\t%prog [options] /path/to/dir", description=__doc__)
OP.set_defaults(spec_method='all')
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. "
+ u"La correspondance est tentée sur le nom du fichier avec la fonction match(). "
+ u"Penser à inclure l'ancre $ pour matcher le nom entier du fichier. "
+ u"Si l'expression régulière contient un caractère '/', la correspondance peut se faire sur le chemin complet du fichier. "
+ u"Pour simplifier, par défaut seule la partie située après le chemin de base est matchée, sauf si l'expression commence par '^', auquel cas la correspondance se fait sur le chemin complet du fichier. "
+ u"L'expression régulière peut définir des groupes qui sont utilisées pour l'extraction des variables."
+ u"\n Il est possible de spécifier cette option plusieurs fois."
+ u"\n Note: 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. "
+ u"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('--all-specs', dest='spec_method', action='store_const', const='all',
help=u"Avec l'option -e, tester toutes les expressions régulières dans l'ordre de définition. C'est le comportement par défaut.")
OP.add_option('--first-spec', dest='spec_method', action='store_const', const='first',
help=u"Avec l'option -e, arrêter à la première expression régulière qui correspond")
OP.add_option('--glob', dest='pattern', action='callback', callback=add_glob, type='string',
help=u"Comme --spec, mais en utilisant les patterns du shell *, ?, [seq], [!seq]. "
+ u"Si l'expression contient un caractère '/', la correspondance se fait sur le chemin complet du fichier avec le module glob. "
+ u"Cette précision est importante notamment pour la recherche de fichiers dont le nom commence par un point. "
+ u"Si l'expression ne contient pas le caractère '/', la correspondance est faite uniquement sur le nom de fichier avec le module fnmatch, et le point n'a plus de signification particulière. "
+ u"Cette méthode de correspondance ne permet pas de définir des groupes et ne permet donc pas d'extraire des variables.")
OP.add_option('-E', '--allow-spec', dest='spec', action='callback', callback=allow_spec, type='string',
help=u"Ajouter une spécification de fichier qui peut être demandé avec --cgi-path-info. "
+ u"Ces fichiers ne sont pas inclus dans la liste.")
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. "
+ u"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<NAME> pour inclure respectivement le groupe numéro N et le groupe nommé NAME de l'expression régulière --spec."
+ u"\n Cette option peut être spécifiée plusieurs fois. "
+ u"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. "
+ u"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<NAME> pour inclure respectivement le groupe numéro N et le groupe nommé NAME de l'expression régulière --spec."
+ u"\n Cette option ne peut être spécifiée qu'une seule fois par option --spec")
OP.add_option('-b', '--break-on', dest='break_on', action='callback', callback=set_break_on, type='string',
2017-04-06 22:32:53 +04:00
help=u"Spécifier une expression qui permet de partitionner la liste des fichiers au sein d'un même groupe. "
+ u"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<NAME> pour inclure respectivement le groupe numéro N et le groupe nommé NAME de l'expression régulière --spec."
+ u"\n Bien que ce ne soit pas une obligation, il est logique de trier la liste sur cette expression pour que les groupes de fichiers soient ensembles dans la liste."
+ u"\n Cette option ne peut être spécifiée qu'une seule fois par option --spec")
2015-12-07 12:26:37 +04:00
OP.add_option('-s', '--sort', dest='sortby',
help=u"Spécifier le champ sur lequel trier ainsi que le type de tri à utiliser. "
+ u"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. "
+ u"Si un champ est spécifié mais que le type de tri ne l'est pas, la valeur par défaut est AC. "
+ u"Si cette option n'est pas spécifiée, le tri par défaut est 'mtime:DN'."
+ u"\n Il est possible de spécifier plusieurs champs pour le tri en les séparant par des virgules.")
2015-12-07 12:26:37 +04:00
OP.add_option('--cgi', dest='cgi_mode', action='store_true',
help=u"Activer le mode CGI. "
+ u"Ce mode est automatiquement activé si la variable d'environnement REQUEST_METHOD existe.")
OP.add_option('--cgi-allow-cache', dest='cgi_allow_cache', action='store_true',
help=u"En mode CGI, ne pas rajouter les en-tête désactivant la mise en cache du résultat.")
OP.add_option('--cgi-content-disposition', '--cgicd', dest='content-disposition', action='callback', callback=set_content_disposition, type='string',
help=u"En mode CGI, forcer la disposition de contenu avec lequel servir le fichier. "
+ u"Les valeurs valides sont attachment (par défaut), inline, none. "
+ u"Si cette option est spécifiée après une option --spec ou --glob, elle ne s'applique qu'aux fichiers qui correspondent à ce modèle")
OP.add_option('--cgi-content-type', '--cgict', dest='content-type', action='callback', callback=set_content_type, type='string',
2017-04-06 22:32:53 +04:00
help=u"En mode CGI, forcer le type de contenu avec lequel servir le fichier. "
+ u"Si cette option est spécifiée après une option --spec ou --glob, elle ne s'applique qu'aux fichiers qui correspondent à ce modèle")
OP.add_option('--cgi-content-charset', '--cgicc', dest='content-charset', action='callback', callback=set_content_charset, type='string',
2017-04-06 22:32:53 +04:00
help=u"En mode CGI, forcer l'encoding de contenu avec lequel servir le fichier. "
+ u"Si cette option est spécifiée après une option --spec ou --glob, elle ne s'applique qu'aux fichiers qui correspondent à ce modèle")
OP.add_option('-P', '--cgi-path-info', dest='path_info',
help=u"Spécifier un chemin d'un fichier à télécharger. "
+ u"En mode CGI, cette valeur est prise dans la variable d'environnement PATH_INFO")
OP.add_option('-Q', '--cgi-query-string', dest='query_string',
help=u"Spécifier la valeur de QUERY_STRING pour provisionner l'environnement du fichier résultat. "
+ u"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. "
+ u"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 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é. "
+ u"Seuls les fichiers du groupe sont affichés. "
+ u"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 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é. "
+ u"En mode CGI, cette option est automatiquement activée si QUERY_STRING contient le paramètre xslt=XSLT."
+ 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. "
+ u"Il est possible de spécifier plusieurs paramètres en les séparant par des virgules. "
+ u"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. "
+ u"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.")
2015-12-07 12:26:37 +04:00
o, args = OP.parse_args()
filters = env['filters']
filter = env['filter']
sortby = o.sortby
2015-12-07 12:26:37 +04:00
environ = os.environ
cgi_mode = o.cgi_mode
cgi_cache = o.cgi_allow_cache and True or False
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
2015-12-07 12:26:37 +04:00
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')
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
if cgi_script_name is None and 'SCRIPT_NAME' in environ : cgi_script_name = environ.get('SCRIPT_NAME')
cgi_select_group = None
if 'group' in form and cgi_select_group is None: cgi_select_group = form.getfirst("group")
if cgi_select_group is None: cgi_select_group = o.select_group
cgi_xslt = None
if 'xslt' in form and cgi_xslt is None: cgi_xslt = form.getfirst("xslt")
if cgi_xslt is None: cgi_xslt = o.xslt
else:
cgi_path_info = o.path_info
cgi_query_string = o.query_string
cgi_script_name = o.script_name
cgi_select_group = o.select_group
cgi_xslt = o.xslt
2015-12-07 12:26:37 +04:00
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, o.spec_method))
2015-12-07 12:26:37 +04:00
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, sauf s'il n'y a
# qu'une seul groupe défini
if len(fgroups) == 1: cgi_select_group = fgroups[0]
else: cgi_select_group = ''
2015-12-07 12:26:37 +04:00
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:
if not cgi_cache: cgi_nocache()
print "Content-Type: text/plain; charset=utf-8"
2015-12-07 12:26:37 +04:00
print
print "Impossible de trouver le fichier %s" % cgi_path_info
sys.exit(1)
2015-12-07 12:26:37 +04:00
else:
if cgi_mode:
if not cgi_cache: cgi_nocache()
dlname = file.dlname or file.name
if file.has_key('content_disposition'): content_disposition = file.content_disposition
else: content_disposition = default_content.disposition
if file.has_key('content_type'): content_type = file.content_type
else: content_type = default_content.type
if content_type is None: content_type, encoding = mimetypes.guess_type(dlname, False)
if content_type is None: content_type = "application/octet-stream"
if file.has_key('content_charset'): content_charset = file.content_charset
else: content_charset = default_content.charset
print "Content-Type: %s; charset=%s" % (content_type, content_charset)
if content_disposition is not None:
print "Content-Disposition: %s; filename=\"%s\"" % (content_disposition, dlname)
2015-12-07 12:26:37 +04:00
print
inf = open(file.pf, 'rb')
try: shutil.copyfileobj(inf, sys.stdout)
finally: inf.close()
sys.exit(0)
2015-12-07 12:26:37 +04:00
if cgi_mode:
if not cgi_cache: cgi_nocache()
print "Content-Type: text/xml; charset=utf-8"
2015-12-07 12:26:37 +04:00
print
print_files(files, fgroups, cgi_select_group, cgi_script_name, cgi_xslt,
cgi_query_string, o.includes, o.excludes, o.prefix)
2015-12-07 12:26:37 +04:00
if __name__ == '__main__':
run_cgilsxml()