"cgi" --- Soporte de Interfaz de Entrada Común (CGI)
****************************************************

**Código fuente:** Lib/cgi.py

Obsoleto desde la versión 3.11: The "cgi" module is deprecated (see
**PEP 594** for details and alternatives).

======================================================================

Módulo de soporte para scripts de la Interfaz de Entrada Común (CGI)

Este módulo define una serie de utilidades para el uso de scripts CGI
escritos en Python.


Introducción
============

Un script de CGI es invocado por un servidor HTTP, generalmente para
procesar entradas de usuario entregadas mediante un elemento HTML
"<FORM>" o "<ISINDEX>".

Muy a menudo, los scripts CGI viven en el directorio especial "cgi-
bin" del servidor. El servidor HTTP coloca todo tipo de información
sobre la solicitud (como el nombre de host del cliente, la dirección
URL solicitada, la cadena de búsqueda (query string) y muchas otras
consultas) en el entorno de shell del script, ejecuta el script y
envía la salida del script al cliente.

La entrada del script también está conectada al cliente, y a veces los
datos del formulario se leen de esta manera; en otras ocasiones los
datos del formulario se pasan a través de la parte "cadena de
caracteres de búsqueda (query string)" de la dirección URL.  Este
módulo está diseñado para ocuparse de los diferentes casos y
proporcionar una interfaz más simple al script de Python.  También
proporciona una serie de utilidades que ayudan en la depuración de
scripts, y la última adición es la compatibilidad con cargas de
archivos desde un formulario (si el navegador lo admite).

La salida de un script CGI debe constar de dos secciones, separadas
por una línea en blanco.  La primera sección contiene una serie de
encabezados, indicando al cliente qué tipo de datos sigue.  El código
de Python para generar una sección de encabezado mínima tiene este
aspecto:

   print("Content-Type: text/html")    # HTML is following
   print()                             # blank line, end of headers

La segunda sección suele ser HTML, lo que permite al software cliente
mostrar texto bien formateado con encabezado, imágenes en línea, etc.
Aquí está el código Python que imprime una simple pieza de HTML:

   print("<TITLE>CGI script output</TITLE>")
   print("<H1>This is my first CGI script</H1>")
   print("Hello, world!")


Usando el módulo CGI
====================

Empieza escribiendo "import cgi".

Cuando escribas un nuevo script, considera añadir estas líneas:

   import cgitb
   cgitb.enable()

Esto activa un manejador de excepciones especial que mostrará informes
detallados en el explorador Web si se produce algún error.  Si
prefiere no mostrar en detalle su programa a los usuarios de su
script, puede tener los informes guardados en archivos en su lugar,
con código como este:

   import cgitb
   cgitb.enable(display=0, logdir="/path/to/logdir")

Es muy útil usar esta característica durante el desarrollo de scripts.
Los informes producidos por "cgitb" proporcionan información que puede
ahorrarle mucho tiempo en el seguimiento de errores.  Siempre puede
eliminar la línea "cgitb" más adelante cuando haya probado su script y
esté seguro de que funciona correctamente.

To get at submitted form data, use the "FieldStorage" class. If the
form contains non-ASCII characters, use the *encoding* keyword
parameter set to the value of the encoding defined for the document.
It is usually contained in the META tag in the HEAD section of the
HTML document or by the *Content-Type* header.  This reads the form
contents from the standard input or the environment (depending on the
value of various environment variables set according to the CGI
standard).  Since it may consume standard input, it should be
instantiated only once.

La instancia "FieldStorage" puede ser indexada como un diccionario
Python. Permite las pruebas de afiliación con el operador "in", y
también es compatible con el método de diccionario estándar "keys()" y
la función incorporada "len()".  Los campos de formulario que
contienen cadenas de caracteres vacías son ignoradas y no aparecen en
el diccionario; para mantener estos valores, proporciona un valor
verdadero para el parámetro de palabra clave opcional
*keep_blank_values* al crear la instancia "FieldStorage".

Por ejemplo, el código siguiente (que supone que el encabezado
*Content-Type* y la línea en blanco ya se han impreso) comprueba que
los campos "name" y "addr" son ambos establecidos a una cadena de
caracteres no vacía:

   form = cgi.FieldStorage()
   if "name" not in form or "addr" not in form:
       print("<H1>Error</H1>")
       print("Please fill in the name and addr fields.")
       return
   print("<p>name:", form["name"].value)
   print("<p>addr:", form["addr"].value)
   ...further form processing here...

Aquí los campos, a los que se accede a través de "form[key]", son por
sí mismos las instancias de "FieldStorage" (o "MiniFieldStorage",
dependiendo de la codificación del formulario). El atributo "value" de
la instancia produce el valor de cadena de caracteres del campo.  El
método "getvalue()" retorna este valor de cadena de caracteres
directamente; también acepta un segundo argumento opcional como valor
predeterminado para retornar si la clave solicitada no está presente.

Si los datos del formulario enviados contienen más de un campo con el
mismo nombre, el objeto recuperado por "form[key]" no es una instancia
"FieldStorage" o "MiniFieldStorage", sino una lista de dichas
instancias.  De forma similar, en esta situación, "form.getvalue(key)"
retornaría una lista de cadenas de caracteres. Si espera esta
posibilidad (cuando su formulario HTML contiene múltiples campos con
el mismo nombre), utilice el método "getlist()", que siempre retorna
una lista de valores (para que no sea necesario poner en mayúsculas y
minúsculas en el caso de un solo elemento).  Por ejemplo, este código
concatena cualquier número de campos de nombre de usuario, separados
por comas:

   value = form.getlist("username")
   usernames = ",".join(value)

Si un campo representa un archivo cargado, el acceso al valor a través
del atributo "value" o el método "getvalue()" lee todo el archivo en
memoria como bytes.  Puede que esto no sea lo que quiera que ocurra.
Puede probar un archivo cargado probando el atributo "filename" o el
atributo "file".  Después, puede leer los datos del atributo "file"
antes de que se cierre automáticamente como parte de la recolección de
elementos no utilizados de la instancia "FieldStorage" (los métodos
"read()" y "readline()" retornarán bytes):

   fileitem = form["userfile"]
   if fileitem.file:
       # It's an uploaded file; count lines
       linecount = 0
       while True:
           line = fileitem.file.readline()
           if not line: break
           linecount = linecount + 1

Los objetos "FieldStorage" también permiten ser usados en una
sentencia "with" , lo que automáticamente los cerrará cuando termine
la sentencia.

Si un error es encontrado al obtener el contenido de un archivo
cargado (por ejemplo, cuando el usuario interrumpe el envío del
formulario haciendo clic en un botón Atrás o Cancelar), el atributo
"done" del objeto para el campo se establecerá en el valor -1.

El borrador de archivo de carga estándar presenta la posibilidad de
cargar varios archivos desde un campo (utilizando una codificación
recursiva *multipart/**). Cuando esto ocurre, el elemento será un
elemento similar a un diccionario "FieldStorage". Esto se puede
determinar probando su atributo "type", que debe ser *multipart/form-
data* (o tal vez otro tipo MIME que coincida *multipart/**).  En este
caso, se puede iterar recursivamente al igual que el objeto de
formulario de nivel superior.

Cuando se envía un formulario en el formato "antiguo" (como la cadena
de caracteres de consulta (query string) o como una sola parte de
datos de tipo *application/x-www-form-urlencoded*), los elementos
serán realmente instancias de la clase "MiniFieldStorage".  En este
caso, los atributos "list", "file" y "filename" siempre son "None".

Un formulario enviado a través de POST que también tiene una cadena de
caracteres de consulta (query string) contendrá los elementos
"FieldStorage" y "MiniFieldStorage".

Distinto en la versión 3.4: El atributo  "file" se cierra
automáticamente con el recolector de basura de la instancia creada
"FieldStorage".

Distinto en la versión 3.5: Agregado soporte para el protocolo de
administrador de contexto a la clase "FieldStorage" .


Interfaz de Nivel Superior
==========================

La sección anterior explica cómo leer datos de un formulario CGI
usando la clase "FieldStorage". Esta sección describe un nivel de
interfaz superior que se añadió a esta clase para permitir que uno lo
haga de una manera más legible e intuitiva. La interfaz no hace que
las técnicas descritas en las secciones anteriores estén obsoletas --
por ejemplo, siguen siendo útiles para procesar la carga de archivos
de manera eficiente.

La interfaz consiste en dos métodos simples. Usando los métodos puedes
procesar datos de formulario de una manera genérica, sin la necesidad
de preocuparte si solo se publicaron uno o más valores con un solo
nombre.

En la sección anterior, aprendiste a escribir el siguiente código cada
vez que esperabas que un usuario publicara más de un valor con un
nombre:

   item = form.getvalue("item")
   if isinstance(item, list):
       # The user is requesting more than one item.
   else:
       # The user is requesting only one item.

Esta situación es común, por ejemplo, cuando un formulario contiene un
grupo de múltiples casillas de verificación (checkboxes) con el mismo
nombre:

   <input type="checkbox" name="item" value="1" />
   <input type="checkbox" name="item" value="2" />

En la mayoría de las situaciones, sin embargo, solo hay un control de
formulario con un nombre determinado en un formulario y así, espera y
solo necesita un valor asociado con este nombre.  Así que escribe un
script que contiene, por ejemplo, este código:

   user = form.getvalue("user").upper()

El problema con el código es que nunca debe esperar que un cliente
proporcione una entrada válida a los scripts.  Por ejemplo, si un
usuario curioso anexa otro par "user=foo" a la cadena de caracteres de
consulta (query string), el script se bloquearía, porque en esta
situación la llamada al método "getvalue("user")" retorna una lista en
lugar de una cadena de caracteres.  Llamar al método "upper()" en una
lista no es válido (ya que las listas no tienen un método con este
nombre) y se produce una excepción "AttributeError".

Por lo tanto, la forma adecuada de leer los valores de datos de
formulario era usar siempre el código que comprueba si el valor
obtenido es un valor único o una lista de valores.  Eso es molesto y
conduce a scripts menos legibles.

Un enfoque más conveniente es utilizar los métodos "getfirst()" y
"getlist()" proporcionados por esta interfaz de nivel superior.

FieldStorage.getfirst(name, default=None)

   Este método siempre retorna solo un valor asociado con el campo de
   formulario *name*. El método retorna solo el primer valor en caso
   de que se registraran más valores con dicho nombre.  Tenga en
   cuenta que el orden en que se reciben los valores puede variar de
   un navegador a otro y no debe darse por sentado. [1]  Si no existe
   ningún campo o valor de formulario, el método retorna el valor
   especificado por el parámetro opcional *default*.  Este parámetro
   tiene como valor predeterminado "None" si no se especifica.

FieldStorage.getlist(name)

   Este método siempre retorna una lista de valores asociados con el
   campo de formulario *name*. El método retorna una lista vacía si no
   existe tal campo o valor de formulario para *name*.  Retorna una
   lista que consta de un elemento si solo existe un valor de este
   tipo.

Usando estos métodos puede escribir código compacto agradable:

   import cgi
   form = cgi.FieldStorage()
   user = form.getfirst("user", "").upper()    # This way it's safe.
   for item in form.getlist("item"):
       do_something(item)


Funciones
=========

Estas son útiles si desea más control, o si desea emplear algunos de
los algoritmos implementados en este módulo en otras circunstancias.

cgi.parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False, separator="&")

   Analiza una consulta en el entorno o desde un archivo (el archivo
   predeterminado es "sys.stdin"). Los parámetros *keep_blank_values*,
   *strict_parsing* y *separator* se pasan a "urllib.parse.parse_qs()"
   sin cambios.

cgi.parse_multipart(fp, pdict, encoding="utf-8", errors="replace", separator="&")

   Analiza la entrada de tipo *multipart/form-data* (para  cargas de
   archivos). Los argumentos son *fp* para el archivo de entrada,
   *pdict* para un diccionario que contiene otros parámetros en el
   encabezado *Content-Type* y *encoding*, la codificación de la
   solicitud.

   Retorna un diccionario al igual que "urllib.parse.parse_qs()": las
   claves son los nombres de campo, cada valor es una lista de valores
   para ese campo. Para los campos que no son de archivo, el valor es
   una lista de cadenas de caracteres.

   Esto es fácil de usar pero no muy bueno si espera que se carguen
   megabytes --- en ese caso, utilice la clase "FieldStorage" en su
   lugar, que es mucho más flexible.

   Distinto en la versión 3.7: Se agregaron los parámetros *encoding*
   y *errors*.  Para los campos que no son de archivo, el valor es
   ahora una lista de cadenas de caracteres, no bytes.

   Distinto en la versión 3.9.2: Agregado el parámetro *separator*.

cgi.parse_header(string)

   Analiza un encabezado MIME (como *Content-Type*) en un valor
   principal y un diccionario de parámetros.

cgi.test()

   Robust test CGI script, usable as main program. Writes minimal HTTP
   headers and formats all information provided to the script in HTML
   format.

cgi.print_environ()

   Da formato al entorno del shell en HTML.

cgi.print_form(form)

   Da formato a un formulario en HTML.

cgi.print_directory()

   Da formato al directorio actual en HTML.

cgi.print_environ_usage()

   Imprime una lista de variables de entorno útiles (utilizadas por
   CGI) en HTML.


Preocuparse por la seguridad
============================

There's one important rule: if you invoke an external program (via
"os.system()", "os.popen()" or other functions with similar
functionality), make very sure you don't pass arbitrary strings
received from the client to the shell.  This is a well-known security
hole whereby clever hackers anywhere on the Web can exploit a gullible
CGI script to invoke arbitrary shell commands.  Even parts of the URL
or field names cannot be trusted, since the request doesn't have to
come from your form!

Para estar en el lado seguro, si debe pasar una cadena de caracteres
de un formulario a un comando de shell, debe asegurarse de que la
cadena de caracteres contiene solo caracteres alfanuméricos, guiones,
guiones bajos y puntos.


Instalando su script de CGI en un sistema Unix
==============================================

Lea la documentación del servidor HTTP y consulte con el administrador
del sistema local para encontrar el directorio donde se deben instalar
los scripts CGI; por lo general, esto se encuentra en un directorio
"cgi-bin" en el árbol del servidor.

Asegúrese de que el script es legible y ejecutable por "otros"; el
modo de archivo Unix debe ser octal "0o755" (utilice "chmod 0755
filename").  Asegúrese de que la primera línea del script contiene
"#!" a partir de la columna 1 seguida del nombre de ruta del
intérprete de Python, por ejemplo:

   #!/usr/local/bin/python

Asegúrese que el intérprete de Python exista y sea ejecutable por
"otros".

Asegúrese de que cualquier archivo que su script necesite leer o
escribir sea legible o tenga permiso de escritura, respectivamente,
por "otros" --- su modo debe ser "0o644" para legible y "0o666" para
escribir.  Esto se debe a que, por razones de seguridad, el servidor
HTTP ejecuta el script como usuario "nadie", sin ningún privilegio
especial.  Sólo puede leer (escribir, ejecutar) archivos que todo el
mundo puede leer (escribir, ejecutar).  El directorio actual en tiempo
de ejecución también es diferente (normalmente es el directorio cgi-
bin del servidor) y el conjunto de variables de entorno también es
diferente de lo que se obtiene al iniciar sesión.  En particular, no
cuente con la ruta de búsqueda del shell para ejecutables ("PATH") o
la ruta de búsqueda del módulo Python ("PYTHONPATH") que se
establecerá en cualquier cosa interesante.

Si necesita cargar módulos desde un directorio el cual no está en la
ruta de búsqueda de módulos predeterminada de Python, puede cambiar la
ruta en su script, antes de importar otros módulos.  Por ejemplo:

   import sys
   sys.path.insert(0, "/usr/home/joe/lib/python")
   sys.path.insert(0, "/usr/local/lib/python")

(¡De esta manera, el directorio insertado de último será buscado
primero!)

Las instrucciones para sistemas que no son Unix pueden variar;
consulte la documentación de su servidor HTTP (usualmente tendrá una
sección sobre scripts CGI).


Probando su script de CGI
=========================

Desafortunadamente, un script CGI generalmente no se ejecutará cuando
lo pruebe desde la línea de comandos, y un script que funcione
perfectamente desde la línea de comandos puede fallar misteriosamente
cuando se ejecuta desde el servidor.  Hay una razón por la que debe
probar el script desde la línea de comandos: si contiene un error de
sintaxis, el intérprete de Python no lo ejecutará en absoluto y lo más
probable es que el servidor HTTP envíe un error críptico al cliente.

Asumiendo que su script no tiene errores de sintaxis, pero este no
funciona, no tiene más opción que leer la siguiente sección.


Depurando scripts de CGI
========================

First of all, check for trivial installation errors --- reading the
section above on installing your CGI script carefully can save you a
lot of time.  If you wonder whether you have understood the
installation procedure correctly, try installing a copy of this module
file ("cgi.py") as a CGI script.  When invoked as a script, the file
will dump its environment and the contents of the form in HTML format.
Give it the right mode etc., and send it a request.  If it's installed
in the standard "cgi-bin" directory, it should be possible to send it
a request by entering a URL into your browser of the form:

   http://yourhostname/cgi-bin/cgi.py?name=Joe+Blow&addr=At+Home

Si esto da un error de tipo 404, el servidor no puede encontrar el
script -- quizás necesite instalarlo en un directorio diferente. Si
este da otro error, hay un problema con la instalación que debería
intentar solucionar antes de continuar. Si obtiene una lista bien
formateada del entorno y el contenido del formulario (en este ejemplo,
los campos deberían estar listados como "addr" con el valor "At Home"
y "name" con el valor "Joe Blow"), el script "cgi.py" ha sido
instalado correctamente. Si sigue el mismo procedimiento para su
propio script, ya debería poder depurarlo.

El siguiente paso podría ser llamar a la función "test()" del módulo
"cgi" de su script: reemplace su código principal con la declaración
única

   cgi.test()

Esto debería producir los mismos resultados que los obtenidos al
instalar el archivo "cgi.py" en sí.

Cuando un script de Python normal lanza una excepción no controlada
(por cualquier razón: de un error tipográfico en un nombre de módulo,
un archivo que no se puede abrir, etc.), el intérprete de Python
imprime un traceback sutil y sale.  Aunque el intérprete de Python
seguirá haciendo esto cuando el script CGI lance una excepción, lo más
probable es que el traceback termine en uno de los archivos de
registro del servidor HTTP o se descarte por completo.

Afortunadamente, una vez que haya logrado que su script ejecute
*algún* código, puede enviar fácilmente tracebacks al navegador web
utilizando el módulo "cgitb". Si aún no lo ha hecho, solo añada las
líneas:

   import cgitb
   cgitb.enable()

al principio de su script. Luego intente ejecutarlo de nuevo; cuando
un problema ocurra, debería ver un informe detallado que probablemente
muestre la causa del error.

Si sospecha que puede haber un problema al importar el módulo "cgitb",
puede usar un enfoque aún más robusto (que solo usa módulos
integrados):

   import sys
   sys.stderr = sys.stdout
   print("Content-Type: text/plain")
   print()
   ...your code here...

Esto se basa en el intérprete de Python para imprimir el traceback.
El tipo de contenido de la salida se establece en texto plano, lo que
deshabilita todo el procesamiento HTML.  Si el script funciona, el
cliente mostrará el código HTML sin formato.  Si lanza una excepción,
lo más probable es que después que se hayan impreso las dos primeras
líneas, se mostrará un traceback. Dado que no se está realizando
ninguna interpretación HTML, el traceback será legible.


Problemas comunes y soluciones
==============================

* La mayoría de servidores HTTP almacenan en búfer la salida de los
  scripts CGI hasta que el script esté completado. Esto significa que
  no es posible mostrar un reporte del progreso en la parte del
  cliente mientras que el script se esté ejecutando.

* Compruebe las instrucciones de instalación anteriores.

* Verifique los archivos de registro (logs) del servidor HTTP. (¡Usar
  "tail -f logfile" en una ventana separada puede ser útil!)

* Siempre verifique un script para encontrar errores de sintaxis
  primero, haciendo algo como "python script.py".

* Si su script no tiene ningún error sintáctico, pruebe añadiendo
  "import cgitb; cgitb.enable()" en la parte superior del script.

* Cuando se invoquen programas externos, asegúrese de que pueden ser
  encontrados. Generalmente esto significa usar nombres de ruta
  absolutos --- "PATH" generalmente no se establece en un valor útil
  en un script CGI.

* Al leer o escribir archivos externos, asegúrese de que puedan ser
  leídas o escritas por el userid por el que su script CGI se va a
  ejecutar: es típico que esto sea el userid bajo el que el servidor
  web se está ejecutando, o algún userid especificado explícitamente
  por la función "suexec" del servidor web.

* No intente darle un modo set-uid a un script CGI. Esto no funciona
  en la mayoría de sistemas, además de ser un riesgo de seguridad.

-[ Notas al pie ]-

[1] Tenga en cuenta que algunas versiones recientes de las
    especificaciones de HTML establecen en qué orden se deben
    suministrar los valores de campo, pero saber si se recibió una
    solicitud de un navegador adaptado, o incluso desde un navegador
    siquiera, es tedioso y propenso a errores.
