"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 gestor de excepciones especial que mostrará informes
detallados en el explorador web si se produce algún error.  Si
prefiere no mostrar los detalles de su programa a los usuarios de su
script, puede tener los informes guardados en archivos en su lugar,
como se muestra en el siguiente código:

   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.

Para llegar a los datos de formulario enviados usa la clase
"FieldStorage". Si el formulario contiene caracteres que no sean
ASCII, usa el parámetro de palabra clave *encoding* establecido en el
valor del codificado definido para el documento. Éste está usualmente
contenido en la etiqueta META en la sección HEAD del documento HTML, o
en el encabezado *Content-Type*. Esto lee los contenidos del
formulario desde la entrada estándar o el ambiente (dependiendo de los
valores de varias variables de entorno establecidas de acuerdo del
estándar CGI). Dado que podría consumir entrada estándar, sólo se
debería instanciar una sola vez.

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.10: 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()

   Script CGI de prueba robusto, usable como programa principal.
   Escribe encabezados HTTP mínimos y formatea toda la información
   proporcionada hacia el script en formato HTML.

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
============================

Hay una regla importante: si invoca un programa externo (a través de
"os.system()", "os.popen()" u otras funciones con una funcionalidad
similar), asegúrese bien de no pasar strings arbitrarios recibidos
desde el cliente hacia la shell.  Este es una brecha de seguridad muy
conocida por la que los hackers inteligentes en cualquier lugar de la
web pueden explotar un inocente script CGI para invocar comandos de
shell arbitrarios.  Incluso partes de la URL o nombres de campo pueden
no ser confiables, ya que la solicitud no tiene que venir de su
formulario!

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
========================

En primer lugar, compruebe si hay errores de instalación triviales ---
leer la sección anterior sobre la instalación cuidadosa de su script
CGI puede ahorrarle mucho tiempo.  Si se pregunta si ha entendido
correctamente el procedimiento de instalación, intente instalando una
copia de este archivo de módulo ("cgi.py") como un script CGI.  Cuando
se invoca como un script, el archivo cambiará su entorno y el
contenido del formulario en formato HTML. Dele el modo correcto, etc.,
y envíe una solicitud.  Si está instalado en el directorio estándar
"cgi-bin", debería ser posible enviarle una solicitud introduciendo
una URL en su navegador de la forma:

   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.
