# Web: motor HTTP, interfaz y panel de administración

> Diseño previsto; aún no implementado.

## Por qué un motor HTTP propio

- **Sin administrador.** La 2.0 usaba `HttpListener`, que en Windows va sobre http.sys:
  escuchar fuera de `localhost` exige ser administrador o reservar la URL con `netsh` (que
  también lo exige). Cambiar la ruta (un "directorio virtual") no lo evita. Un `Socket` propio
  no necesita permisos en Windows con ningún puerto; en Linux y macOS los puertos por debajo de
  1024 siguen requiriendo privilegios, por eso los puertos por defecto son altos.
- **Tolerancia con clientes antiguos.** Los navegadores de los 90 envían peticiones que los
  servidores modernos rechazan (saltos de línea solo `LF`, HTTP/1.0 sin `Host`, cabeceras poco
  normales). Con un motor propio se aceptan.
- **Native AOT.** Sin ASP.NET Core, `sunfish-cli` puede ser un binario nativo pequeño.

## Alcance del motor

- **Versiones:** HTTP/0.9 (`GET /ruta` sin versión: solo el cuerpo), HTTP/1.0 y HTTP/1.1.
- **Métodos:** `GET`, `HEAD`, `POST` (formularios y subidas), `PUT` (subidas modernas) y los de
  WebDAV ([FTP-Y-WEBDAV.md](FTP-Y-WEBDAV.md)).
- **Analizador tolerante:** acepta `CRLF` o `LF`; nombres de cabecera sin distinguir mayúsculas;
  `Host` opcional en 1.0.
- **Conexiones:** keep-alive en 1.1 y en 1.0 si se pide `Connection: keep-alive`; si no, se
  cierra al terminar.
- **Respuestas:**
  - Los ficheros llevan siempre `Content-Length`: los clientes antiguos no conocen `chunked`, y
    así muestran el progreso.
  - Las páginas generadas se envían con `chunked` a clientes 1.1 y cerrando la conexión a los 1.0.
- **Rangos** (`Range: bytes=`, uno solo) para reanudar descargas; `If-Modified-Since` → 304.
- **Cuerpos:** `Content-Length` o `chunked`; `Expect: 100-continue`.
- **Autenticación:** HTTP Basic (la soportan hasta Mosaic y los primeros Netscape).
- **Límites:**
  - Tamaño de la línea de petición y de las cabeceras (8 KB).
  - Plazos de inactividad (contra conexiones lentas a propósito).
  - Conexiones por IP.
  - Tamaño máximo de subida, opcional.
- **Puerto por defecto:** 8080, configurable; IPv4 e IPv6.
- **Sin TLS** (fuera de alcance; para eso existe Arrejollal).

## Interfaz web

### Dos modos con la misma página

| Modo | Para quién | Qué cambia |
|---|---|---|
| **Clásico** | Navegadores antiguos (por defecto si el User-Agent no es moderno) | HTML 3.2/4.01 Transitional, maquetado con tablas, sin JavaScript, ISO-8859-1 |
| **Moderno** | Navegadores actuales | La misma estructura, UTF-8, un CSS sencillo y un JS pequeño para arrastrar y soltar y para subir por trozos con progreso |

- El servidor decide por el User-Agent. El usuario puede forzar el modo con un enlace
  (`?ui=classic` / `?ui=modern`), que se recuerda en una cookie si el navegador las admite.
- El modo clásico **no incluye ningún `<script>`**: algunos navegadores antiguos muestran un
  diálogo de error por cada script que no entienden.
- Todo funciona sin JS: el JS del modo moderno solo mejora lo que ya hacen los formularios.
- Sin recursos externos (la 2.0 cargaba fuentes e iconos de Internet; en una LAN sin salida se
  veía rota). Iconos en **GIF** incrustados: IE3, Netscape 1–3 y Mosaic no muestran PNG.

### Juego de caracteres

- **Modo clásico:** la página se declara ISO-8859-1.
  - Los caracteres Latin-1 se escriben como entidades HTML (`&aacute;`), que entienden incluso
    los navegadores de HTML 2.0.
  - Los demás caracteres se muestran como `?` (o transliterados cuando haya equivalente).
- **Modo moderno:** UTF-8.
- **Los enlaces llevan siempre los bytes UTF-8 reales en `%XX`**, así que funcionan aunque el
  texto se vea transliterado: el navegador devuelve los mismos bytes al pedir.
- **Lo que envía un navegador antiguo** (nombres de carpeta nueva, nombres en subidas) llega en
  el juego de caracteres de la página o en el del sistema.
  - El servidor intenta UTF-8 y, si no es válido, usa el juego configurado para clientes
    antiguos (ISO-8859-1 por defecto; MacRoman para Mac clásico).
  - **Pendiente** de probar con clientes reales.

### Navegador de directorios

- **Tabla** con icono, nombre (enlace), tamaño, fecha y acciones. Se ordena por nombre, tamaño o
  fecha con un enlace en la cabecera (`?sort=`), sin JS.
- **Migas de pan** con la ruta y enlace a la raíz.
- **Formularios**, según los permisos del share:
  - subir fichero (`multipart/form-data`, RFC 1867, desde Netscape 2 e IE3/IE4);
  - crear carpeta;
  - descargar en ZIP.
- **Renombrar y borrar** llevan a una página de confirmación con un formulario POST. Nunca hay
  cambios por GET (la 2.0 borraba con `?action=delete`).
- **Mac clásico:** con `Mac = true`, enlaces `[.bin]` y `[.hqx]` junto a cada fichero (ver
  [MAC-CLASICO.md](MAC-CLASICO.md)).
- **Escapado:** todo nombre que se escribe en el HTML se escapa (la 2.0 no lo hacía).

### Subidas

- **Multipart:** el analizador trabaja **en flujo** y escribe directamente a disco; los límites
  pueden quedar partidos entre búferes. La 2.0 leía byte a byte y lo guardaba todo en memoria.
- **Nombre del fichero:** se queda solo el nombre. IE antiguo envía la ruta completa
  (`C:\WINDOWS\fichero.txt`) y Mac clásico puede usar `:` como separador.
- **Escritura segura:** se escribe a un temporal en la misma carpeta y se renombra al terminar.
  Una subida cortada no deja un fichero a medias ni pisa el existente.
- **Modo moderno:** `PUT` por trozos con desplazamiento (como hacía la 2.0) para ficheros grandes
  y progreso. El trozo final confirma y renombra.
- Respeta `Upload` y `Overwrite`; comprueba el espacio libre si el tamaño se conoce de antemano.

### Descargar carpetas en ZIP

- Se genera **en flujo**, sin fichero temporal (la 2.0 creaba un ZIP temporal completo).
- **Compatible con descompresores antiguos** (PKZIP 2.x, StuffIt):
  - Por defecto, método **Stored** con el CRC calculado antes de enviar. No necesita
    *data descriptors* y permite enviar `Content-Length`, a costa de leer dos veces cada fichero.
  - Opcionalmente **Deflate** con *data descriptors*; el tamaño final no se conoce de antemano.
  - Nombres en CP437 si son representables; si no, UTF-8 con su indicador.
  - ZIP64 solo si se superan los 4 GiB.

### Autodescarga del servidor

`/$sunfish/download` entrega el propio ejecutable que está sirviendo (como hacía la 2.0), para
repartirlo por la LAN. Se desactiva con `SelfDownload = false`.

## Panel de administración

En `/$sunfish/admin/`, **desactivado por defecto**.

- **Acceso:** usuario `admin` con contraseña (HTTP Basic); hash PBKDF2 en `[Admin]`.
- **`AllowFrom`:** lista de direcciones o redes desde las que se permite. Por defecto, solo el
  propio equipo.
- **Funciones**, las mismas que la GUI:
  - estado y conexiones;
  - activar o desactivar HTTP y FTP;
  - shares (alta, baja, edición);
  - usuarios y contraseñas;
  - registro.
- **HTML clásico** también, para poder administrar desde cualquier navegador.
- **Formularios POST con *token* anti-CSRF** (ver [SEGURIDAD.md](SEGURIDAD.md)).
- Los cambios se guardan en el fichero de configuración al momento.
- Aviso visible: la contraseña viaja en claro por la red.
