diff --git a/.gitattributes b/.gitattributes index 1ff0c42..af66e1f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,63 +1,6 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain +* text=auto eol=crlf +*.sh text eol=lf +*.png binary +*.gif binary +*.ico binary +*.bmp binary diff --git a/.gitignore b/.gitignore index def2805..2d6b5d0 100644 --- a/.gitignore +++ b/.gitignore @@ -194,3 +194,10 @@ !/Sunfish/bin !/Sunfish/bin/Debug !/Sunfish/bin/Debug/$sunfish + +# Sunfish 3: binarios generados por build.sh / build.ps1 y configuracion local +/Sunfish.exe +/sunfish-cli +/sunfish-cli.exe +/sunfish.conf +publish/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..be24e74 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,44 @@ +# Sunfish — AGENTS.md + +Servidor web y de ficheros ligero y portable. Publica carpetas a la vez por web (HTML4 apto para +navegadores de los 90), FTP y WebDAV, para mover ficheros entre equipos modernos y máquinas muy +antiguas en redes de confianza. La versión 3 es una reescritura completa de la 2.0. + +## Decisiones tomadas +- **C# / .NET 10.** Toda la lógica en `src/Sunfish.Core` (compatible con AOT); aplicaciones + finas encima. Ver `DOC/ARQUITECTURA.md`. +- **Dos ejecutables:** + - `Sunfish.exe` (WinForms, Windows): un solo `.exe` (`PublishSingleFile`), porque WinForms no + admite AOT. + - `sunfish-cli` (TUI/consola, Windows/Linux/macOS): Native AOT. + - Más adelante, interfaces GTK y macOS sobre el mismo Core. +- **Motor HTTP propio sobre `Socket`** (ni `HttpListener` ni Kestrel): sin administrador, + tolerante con clientes antiguos, permite AOT. + - En este proyecto se publica con AOT aunque sea un servidor web: la excepción general para + servidores web no aplica porque no se usa ASP.NET. +- **Un share = carpeta + location**, servida a la vez por web, FTP y WebDAV. + - Gana la *location* más específica (por segmentos, como las *locations* de prefijo de nginx). + - Si hay índice se sirve; si no, navegador de directorios. Ver `DOC/SHARES-Y-RUTAS.md`. +- **Web en modo clásico** (HTML 3.2/4.01, sin JS, ISO-8859-1 con entidades, GIF) y **modo + moderno** como mejora progresiva. Subidas por formulario multipart. Ver `DOC/WEB.md`. +- **FTP** activo/pasivo con juegos de caracteres antiguos; **WebDAV** clase 1 + LOCK simulado. + Ver `DOC/FTP-Y-WEBDAV.md`. +- **Mac clásico:** MacBinary/BinHex al subir y bajar, forks guardados como AppleDouble (`._`). + Ver `DOC/MAC-CLASICO.md`. +- **Panel de administración web** con contraseña, desactivado por defecto. +- **Sin TLS a propósito.** Se eliminan la ejecución en el servidor, compartir pantalla, los + plugins y el requisito de administrador. Ver `DOC/SEGURIDAD.md` y `DOC/LEGACY-2.0.md`. +- **Configuración** en INI (`sunfish.conf`, claves en inglés), con importación de la 2.0. + Textos en español e inglés (`SUNFISH_LANG`). + +## Estado +- **Solo documentación** de la 3. El código de `Sunfish/` y `Sunfish Test Plugin/` es la 2.0 + (.NET Framework 4.5): no se porta y se eliminará al empezar la reescritura. +- **Remoto** actual privado, rama `master`. El README ya apunta a GitHub (rama `main`) para + cuando se publique allí. +- **Decisiones pendientes** listadas en `DOC/README.md`. + +## Compilar +Previsto: `dotnet build`, `dotnet test`, `dotnet run --project src/Sunfish.WinForms` o +`src/Sunfish.Cli`. `build.ps1` compila los dos ejecutables en Windows; `build.sh`, `sunfish-cli` +en Linux/macOS. Ver `DOC/COMPILACION-Y-PRUEBAS.md`. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/DOC/APLICACIONES.md b/DOC/APLICACIONES.md new file mode 100644 index 0000000..916674d --- /dev/null +++ b/DOC/APLICACIONES.md @@ -0,0 +1,93 @@ +# Aplicaciones: WinForms, consola y futuras + +> Diseño previsto; aún no implementado. Todas usan `Sunfish.Core` y el mismo fichero de +> configuración; ninguna contiene lógica de servidor. + +## Lo común a todas + +- **Activar y desactivar** HTTP y FTP por separado; puertos. +- **Shares:** alta, baja, edición de todas las opciones ([SHARES-Y-RUTAS.md](SHARES-Y-RUTAS.md)), + activar/desactivar. +- **Usuarios** y contraseñas; panel de administración (activar, contraseña, `AllowFrom`). +- **Direcciones:** lista de IP locales por interfaz con la URL de cada share (HTTP y FTP), + copiable, y un **código QR** de la URL para abrirla desde un móvil. +- **Registro:** conexiones, descargas, subidas y errores. +- **Modo rápido:** si se pasa una carpeta como argumento (también **arrastrándola sobre el + ejecutable** en el explorador), se comparte al momento. + - Se publica en `/` con listado y solo lectura, se arranca el servidor y se muestran las URLs. + - No se guarda en la configuración salvo que se pida. +- **Textos** en español e inglés según el sistema; `SUNFISH_LANG=es|en` o `--lang` lo fuerzan. + +## `Sunfish.exe` (WinForms, Windows) + +- **Ventana principal:** + - interruptores HTTP/FTP y puertos; + - lista de shares (arrastrar una carpeta a la lista crea un share con `Location = /`, + como en la 2.0); + - botones de añadir, editar y quitar; + - acceso a usuarios y al panel web. +- **Menú contextual de un share:** copiar URL por cada IP (como en la 2.0), abrir en el + navegador, mostrar QR, abrir la carpeta en el Explorador. +- **Diálogo de share** con todas las opciones y avisos (p. ej. `Upload` + `Listing` publica + HTML subido). +- **Pestaña de registro** y lista de conexiones activas. +- **Icono en el área de notificación:** minimizar a la bandeja; opción de iniciar con Windows + (entrada en `HKCU\...\Run`) e iniciar minimizado. +- **Una sola instancia por configuración:** abrir otra vez la trae al frente y le pasa los + argumentos del modo rápido. +- **`WinExe`:** sin ventana de consola. +- **Pendiente:** que el primer arranque ofrezca la regla del cortafuegos de Windows (pedirá + elevación solo para eso) o dejar que Windows lo pregunte por sí mismo. + +## `sunfish-cli` (consola, Windows / Linux / macOS) + +| Cómo se lanza | Modo | +|---|---| +| Sin argumentos, con terminal (doble clic incluido) | **TUI interactiva**: sirve y gestiona | +| Sin argumentos, con stdin/stdout redirigidos | Igual que `serve` | +| `sunfish-cli CARPETA` | Modo rápido (TUI si hay terminal) | +| `sunfish-cli serve` | Sin interfaz; registro por la salida estándar (para servicios) | +| `sunfish-cli ` | Ejecuta y sale | + +**TUI** (ANSI/VT, sin bibliotecas externas): pestañas Estado (IP, URLs, QR con medios bloques), +Shares, Usuarios, Conexiones y Registro. Funciona en Windows Terminal, conhost, terminales de +Unix y por SSH. + +**Subcomandos (propuesta):** + +``` +sunfish-cli serve +sunfish-cli show +sunfish-cli share add NOMBRE CARPETA [--location /ruta] [--listing] [--upload] [--delete] ... +sunfish-cli share set NOMBRE OPCION=VALOR ... +sunfish-cli share del NOMBRE +sunfish-cli share list +sunfish-cli user add NOMBRE (pide la contraseña) +sunfish-cli user passwd NOMBRE +sunfish-cli user del NOMBRE +sunfish-cli admin enable|disable +sunfish-cli admin passwd +sunfish-cli import-v2 [FICHERO] + +Opciones globales: -c, --config FICHERO -l, --log FICHERO --lang es|en +``` + +Como servicio: unidad de systemd o `launchd` con `serve` (ejemplos en el repositorio cuando +exista el código); en Windows, como tarea programada o con una herramienta de servicios +(**pendiente**). + +## Futuras: GTK (Linux) y macOS + +- **Interfaz nativa sobre `Sunfish.Core`:** las mismas funciones que la de WinForms. +- **Opciones a evaluar:** + - GTK 4 con bindings de .NET (Gir.Core, que apunta a ser compatible con AOT); + - macOS con .NET para macOS (AppKit), que admite Native AOT. +- **Mientras tanto:** en Linux y macOS se usa `sunfish-cli`. + +## QR y direcciones + +- **QR propio** en `Sunfish.Core` (sin dependencias): devuelve una matriz de módulos. + - WinForms la pinta en un `Bitmap`. + - La TUI la dibuja con `▀ ▄ █`. +- **Direcciones:** se listan solo las interfaces activas con IPv4 o IPv6 globales/locales, + indicando el nombre y el tipo de la interfaz, como hacía la 2.0. diff --git a/DOC/ARQUITECTURA.md b/DOC/ARQUITECTURA.md new file mode 100644 index 0000000..c7b5e16 --- /dev/null +++ b/DOC/ARQUITECTURA.md @@ -0,0 +1,109 @@ +# Arquitectura (prevista) + +> Sunfish 3 está en diseño: todavía no hay código nuevo. El código de la 2.0 sigue en el +> repositorio hasta que empiece la reescritura (ver [LEGACY-2.0.md](LEGACY-2.0.md)). + +## Idea general + +Una **biblioteca común** con todo el servidor (HTTP, web, FTP, WebDAV, configuración, Mac +clásico) y **aplicaciones finas** encima, una por tipo de interfaz: + +``` + Sunfish.exe (WinForms) sunfish-cli (TUI / consola) futuro: GTK · macOS + │ │ │ + └──────────────┬───────────┴───────────────────────────┘ + │ SunfishHost: arrancar, parar, estado, eventos + ┌──────────┴───────────────────────────────────────────┐ + │ Sunfish.Core │ + │ Config · Shares/rutas · Usuarios · Registro │ + │ HTTP propio ─┬─ Web (HTML4) ─ Admin ─ WebDAV │ + │ FTP ─────────┘ │ + │ Mac clásico (MacBinary, BinHex, AppleDouble) · ZIP │ + │ QR · IP locales · Textos ES/EN │ + └──────────────────────────────────────────────────────┘ +``` + +Las aplicaciones no contienen lógica de servidor: muestran el estado, editan la configuración +y llaman a `SunfishHost`. Así una interfaz GTK o macOS futura solo tiene que hacer eso mismo. + +## Estructura del repositorio + +``` +Sunfish/ +├─ Sunfish.slnx +├─ src/ +│ ├─ Sunfish.Core/ biblioteca (net10.0), compatible con Native AOT +│ ├─ Sunfish.WinForms/ Sunfish.exe (net10.0-windows, WinExe) +│ └─ Sunfish.Cli/ sunfish-cli (net10.0, Native AOT) +│ (futuro) Sunfish.Gtk/, Sunfish.Mac/ +├─ tests/ +│ └─ Sunfish.Tests/ xUnit; InternalsVisibleTo desde Sunfish.Core +├─ DOC/ +├─ build.ps1 Windows: compila Sunfish.exe y sunfish-cli.exe +├─ build.sh Linux y macOS: compila sunfish-cli +├─ run.cmd · run-cli.cmd dotnet run (GUI / consola) para doble clic en Windows +├─ run.sh dotnet run de la consola en Linux y macOS +├─ AGENTS.md · CLAUDE.md +└─ README.md · LICENSE +``` + +## Publicación + +| Ejecutable | Sistemas | Modo | Notas | +|---|---|---|---| +| `sunfish-cli` | win, linux, osx (x64 y arm64) | **Native AOT** | Un binario nativo sin runtime. Se compila en cada sistema | +| `Sunfish.exe` | win-x64, win-arm64 | **Un solo `.exe`** (`PublishSingleFile`) | WinForms no admite Native AOT | + +Para `Sunfish.exe` hay dos variantes: + +- **Autocontenida** (`SelfContained`, `EnableCompressionInSingleFile`): incluye el runtime de + .NET y funciona en cualquier Windows 10/11 sin instalar nada. Es la más portable, pero grande + (del orden de decenas de MB; WinForms no admite *trimming*). +- **Dependiente del framework:** unos pocos MB, pero necesita el .NET Desktop Runtime 10. + +**Pendiente:** medir el tamaño real de las dos y fijar cuál genera `build.ps1` por defecto +(propuesta: autocontenida, con una variable de entorno para la ligera). + +`sunfish-cli` es la opción más portable de verdad: nativo y pequeño en los tres sistemas. + +## Módulos de `Sunfish.Core` + +| Módulo | Responsabilidad | Documento | +|---|---|---| +| `Host` | `SunfishHost`: arranque y parada de los servicios, estado, eventos (registro, conexiones, transferencias) para las interfaces | — | +| `Config` | Fichero INI (leer y escribir conservando comentarios), ubicación, importación de la 2.0 | [SHARES-Y-RUTAS.md](SHARES-Y-RUTAS.md) | +| `Shares` | Árbol de *locations*, resolución por prefijo más largo, directorios virtuales, validación de rutas | [SHARES-Y-RUTAS.md](SHARES-Y-RUTAS.md) | +| `Auth` | Usuarios, hashes PBKDF2, caché de credenciales verificadas, límite de intentos | [SEGURIDAD.md](SEGURIDAD.md) | +| `Http` | Servidor HTTP propio sobre `Socket`: HTTP/0.9, 1.0 y 1.1, rangos, multipart en flujo, Basic | [WEB.md](WEB.md) | +| `Web` | Páginas HTML4, juego de caracteres, iconos GIF, mejora progresiva con JS, autodescarga | [WEB.md](WEB.md) | +| `Admin` | Panel de administración web con contraseña | [WEB.md](WEB.md#panel-de-administración) | +| `WebDav` | Clase 1 + LOCK simulado sobre el mismo motor HTTP | [FTP-Y-WEBDAV.md](FTP-Y-WEBDAV.md) | +| `Ftp` | Servidor FTP: control, datos activo/pasivo, listados, juegos de caracteres | [FTP-Y-WEBDAV.md](FTP-Y-WEBDAV.md) | +| `Mac` | MacBinary I/II/III, BinHex 4.0, AppleDouble, tabla de tipo/creador | [MAC-CLASICO.md](MAC-CLASICO.md) | +| `Zip` | Escritor ZIP en flujo, compatible con descompresores antiguos | [WEB.md](WEB.md#descargar-carpetas-en-zip) | +| `Qr` | Generador de códigos QR propio (para la URL; lo pintan la GUI y la TUI) | [APLICACIONES.md](APLICACIONES.md) | +| `Net` | Lista de IP locales por interfaz, URLs por share | [APLICACIONES.md](APLICACIONES.md) | +| `Strings` | Textos en español e inglés | — | +| `Log` | Registro en memoria (para las interfaces) y opcionalmente a fichero | — | + +Los recursos web (GIF, CSS, JS mínimo) van como `EmbeddedResource` de `Sunfish.Core`: no hay +carpeta `$sunfish` externa ni "sellado". + +## Concurrencia + +- Todo asíncrono con `Socket` y `async`/`await`; ni un hilo por petición ni `Thread.Abort`. +- HTTP: una tarea por conexión, con keep-alive cuando el cliente lo admite. +- FTP: una tarea por conexión de control y otra por cada conexión de datos. +- Límites globales y por IP de conexiones simultáneas; plazos de lectura y escritura. +- Las interfaces reciben los eventos del host en su propio hilo (la GUI hace `Invoke`). + +## Restricciones de Native AOT (para `Sunfish.Core` y `sunfish-cli`) + +- Sin reflexión: nada de plugins, ni plantillas por reflexión (como en la 2.0), ni serialización + por reflexión. Las páginas se generan con código y plantillas simples con diccionarios. +- La importación de la configuración 2.0 (JSON) usa `JsonDocument`, que no necesita reflexión. +- El XML de WebDAV se lee con `XmlReader` y se escribe con `XmlWriter`. +- Los juegos de caracteres antiguos (ISO-8859-1, MacRoman, CP437, CP850, Windows-1252) usan + `CodePagesEncodingProvider`, registrado al arrancar. +- P/Invoke, si hiciera falta, solo con `LibraryImport`. +- `Sunfish.Core` lleva `IsAotCompatible`; los avisos de AOT y *trimming* se tratan como errores. diff --git a/DOC/COMPILACION-Y-PRUEBAS.md b/DOC/COMPILACION-Y-PRUEBAS.md new file mode 100644 index 0000000..b4978e5 --- /dev/null +++ b/DOC/COMPILACION-Y-PRUEBAS.md @@ -0,0 +1,89 @@ +# Compilación, pruebas y estado + +## Estado actual + +- **Sunfish 3 en diseño:** solo hay documentación (`DOC/`, `AGENTS.md`, `README.md`). +- El código de la 2.0 sigue en el repositorio y **no** es la base de la 3: se reescribe desde + cero ([LEGACY-2.0.md](LEGACY-2.0.md)). +- Rama actual `master` en un remoto privado. El README ya da las instrucciones para GitHub + (rama `main`), para cuando se publique allí. + +## Publicación prevista + +| Proyecto | Salida | Propiedades | +|---|---|---| +| `Sunfish.Core` | biblioteca | `IsAotCompatible`, `TreatWarningsAsErrors` para avisos de AOT/trimming | +| `Sunfish.Cli` | `sunfish-cli` / `sunfish-cli.exe` | `PublishAot`, `InvariantGlobalization`, `StripSymbols` | +| `Sunfish.WinForms` | `Sunfish.exe` | `OutputType=WinExe`, `UseWindowsForms`, `PublishSingleFile`, `IncludeNativeLibrariesForSelfExtract`; autocontenido + `EnableCompressionInSingleFile` o dependiente del framework (pendiente, ver [ARQUITECTURA.md](ARQUITECTURA.md#publicación)) | + +- .NET 10. RID: `win-x64`, `win-arm64`, `linux-x64`, `linux-arm64`, `osx-x64`, `osx-arm64`. +- Native AOT no compila de forma cruzada entre sistemas: cada `sunfish-cli` se compila en su + sistema. + +## Requisitos + +- git y el SDK de .NET 10. +- Para `sunfish-cli` (AOT): + - Windows: Visual Studio Build Tools con «Desarrollo para escritorio con C++». + - Linux: `clang`, `ld` y las cabeceras de zlib. + - macOS: Xcode Command Line Tools. +- Para `Sunfish.exe`: solo el SDK. + +## Scripts previstos + +| Script | Función | +|---|---| +| `build.ps1` | Windows. Detecta el RID, comprueba requisitos (no instala nada; da el comando), usa el repo actual o clona/actualiza, publica `Sunfish.exe` y `sunfish-cli.exe`, los deja en la raíz y explica cómo poner `sunfish-cli` en el `PATH`. **Pendiente:** si faltan las herramientas de C++, ¿falla o compila solo la GUI? | +| `build.sh` | Linux y macOS. Lo mismo para `sunfish-cli` (sugerencias para apt, dnf, pacman y brew) | +| `run.cmd` | `dotnet run` de `Sunfish.WinForms`, para doble clic | +| `run-cli.cmd` | `dotnet run` de `Sunfish.Cli`, para doble clic | +| `run.sh` | `dotnet run` de `Sunfish.Cli` en Linux y macOS | + +- Variable para forks: `SUNFISH_REPO_URL`. +- Sin script `publish` aparte: `build.*` ya publica. +- `.gitignore`: `/Sunfish.exe`, `/sunfish-cli`, `/sunfish-cli.exe`, `/sunfish.conf`, `publish/`. + +## Comandos previstos + +``` +dotnet build +dotnet test +dotnet run --project src/Sunfish.WinForms +dotnet run --project src/Sunfish.Cli -- [argumentos] +dotnet publish src/Sunfish.Cli -c Release -r +dotnet publish src/Sunfish.WinForms -c Release -r win-x64 +``` + +## Pruebas automáticas previstas (`tests/Sunfish.Tests`) + +xUnit sobre `net10.0`, con `InternalsVisibleTo` desde `Sunfish.Core`. Las pruebas que tocan +variables de entorno van en una colección sin paralelismo. Las de servidor arrancan +`SunfishHost` en `localhost` con puertos libres y hablan con él por sockets. + +| Área | Qué se prueba | +|---|---| +| HTTP | HTTP/0.9, 1.0 sin `Host`, `LF` solo, cabeceras raras, límites, keep-alive, `chunked`, `Range`, 304, `HEAD` | +| Multipart | Límites partidos entre búferes, varios ficheros, nombres con ruta de Windows o de Mac, subida cortada (no deja restos) | +| Locations | Prefijo más largo, comparación por segmentos, ocultación, directorios virtuales, raíz con y sin menú, `/$sunfish` | +| Rutas | `..`, `%2e%2e`, `\`, `:`, nombres reservados, enlaces que salen del share (con ramas por SO) | +| Permisos | Matriz operación × opción de share × web/FTP/WebDAV; usuarios | +| HTML | Escapado, entidades Latin-1, enlaces `%XX` en UTF-8, modo clásico sin `