From fabf36ff467539ebdb3f17c40c808d27cb670913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Labb=C3=A9?= Date: Mon, 15 Dec 2025 14:49:42 +0100 Subject: [PATCH] Initial commit --- README.md | 203 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 + sshserial.py | 69 ++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 README.md create mode 100644 requirements.txt create mode 100644 sshserial.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..bd37baa --- /dev/null +++ b/README.md @@ -0,0 +1,203 @@ +# SSH-To-MINITEL-SERIAL + +Passerelle **Minitel ⇄ SSH** écrite en **Python**, utilisant **WebSocket** pour interconnecter un **Minitel réel (Magis Club, Minitel 1B, etc.)** avec un **serveur SSH moderne**, tout en respectant les contraintes historiques du **VIDEOTEX**. + +Ce projet permet d'utiliser un Minitel comme **terminal interactif** pour accéder à un shell Unix distant (`bash`, `vi`, `mc`, etc.). + +--- + +## Fonctionnalités + +* Compatibilité **Minitel réel** (M1 / M1B / Magis Club) +* Connexion à un **serveur SSH réseau** +* Passerelle **USB/SERIAL** +* Utilisation correcte de **terminfo Minitel (`mntl.ti`)** +* Gestion des touches Minitel (ENVOI, RETOUR, etc.) +* Mode texte **40 colonnes Videotex** +* Sans Unicode (ISO-8859-1 / `LANG=C`) +* Testé sur Débian 13, mac et Raspberry pi3b sur MagisClus à 9600 Bauds + +--- + +## Architecture + +```text +[Minitel réel] + │ (VIDEOTEX / Série / TCP) + ▼ +[Client WebSocket] + ▼ +[Serveur Python SSHMINITEL] + ├─ WebSocket + ├─ Paramiko (SSH) + ├─ Mapping clavier Minitel + ▼ +[Serveur SSH distant] +``` + +--- + +## Prérequis + +### Serveur passerelle (Python) + +* Python ≥ 3.10 +* `paramiko` +* `pyserial` +## Récupération du projet (git clone) + +Depuis une machine disposant de Git : + +```sh +git clone https://github.com/labbej27/sshserial.git +cd sshminitel +``` +Création d’un environnement virtuel Python (recommandé) : + +```sh +python3 -m venv venv +source venv/bin/activate +``` +Installation des dépendances : +```sh +pip install -r requirements.txt +``` + +### Serveur SSH distant + +* Linux / Unix +* `ncurses` +* Installation du terminfo Minitel (`mntl.ti`) + +--- + +## Installation du terminfo Minitel (OBLIGATOIRE) + +Sur le **serveur SSH** : + +```sh +sudo adduser minitel #Création d'un utilisateur dédié au minitel +nano /home/minitel/.profile +#ajouter à la fin : +export TERM=m1b +export LANG=C +stty -ixon icrnl onlcr -echo +#puis sauvegarder ctrl-o puis ctrl-x +``` +Pourquoi ? +* TERM=m1b → compatibilité Minitel (vous pouvez choisir d'autres minitels comme ) +* LANG=C → pas d'UTF-8 +* icrnl → ENVOI = Entrée +* -echo → évite les caractères en double + +## Se loguer en user minitel : + +```sh +wget http://canal.chez.com/mntl.ti +tic -x mntl.ti +infocmp m1b +``` +doit afficher : +```sh +# Reconstructed via infocmp from file: /home/minitel/.terminfo/m/m1b +m1b|minitel 1-bistandard (in 40cols mode), + am, bw, eslok, hs, hz, mir, + colors#8, cols#40, lines#24, pairs#8, + acsc=0\177j+k+l+m+n+o~q`s_t+u+v+w+x|, bel=^G, + blink=\EH, civis=^T, clear=^L, cnorm=^Q, cr=\r, + cub=\E[%p1%dD, cub1=^H, cud=\E[%p1%dB, cud1=\n, + cuf=\E[%p1%dC, cuf1=^I, cup=\037%p1%'A'%+%c%p2%'A'%+%c, + cuu=\E[%p1%dA, cuu1=^K, dch=\E[%p1%dP, dch1=\E[P, + dl=\E[%p1%dM, dl1=\E[M, dsl=\037@A\030\n, ed=\E[J, el=^X, + el1=\E[1K, flash=\037@A\EW \177\022\177\022P\r\030\n, + fsl=\n, home=^^, il=\E[%p1%dL, il1=\E[L, ind=\n, + iprog=stty -ixon, is1=\E:dS\E;iYA\E;jYC, + is2=\E;`ZQ\E:iC\E:iE\021, kbs@, kcan@, kclr=\E[2J, + kcub1=\E[D, kcud1=\E[B, kcuf1=\E[C, kcuu1=\E[A, kdch1=\E[P, + kdl1=\E[M, kend=^SI, kent@, kf1=^SD, kf10=^Y0, kf11=^Y1, + kf12=^Y/, kf13=^Y{1, kf14=^Y{2, kf15=^Y{3, kf16=^Y{4, + kf17=^Y{5, kf18=^Y{6, kf19=^Y{7, kf2=^SC, kf20=^Y{8, + kf21=^Y{9, kf22=^Y{0, kf23=^Y{*, kf24=^Y{#, kf3=^SF, kf4=^SA, + kf5=^SG, kf6=^SE, kf7=^Y8, kf8=^Y\,, kf9=^Y., khlp@, + khome=\E[H, kich1=\E[4h, kil1=\E[L, knp=^SH, kpp=^SB, krfr@, + lf1=Guide, lf10=Ctrl+0, lf2=Repetition, lf3=Sommaire, + lf4=Envoi, lf5=Correction, lf6=Annulation, lf7=Ctrl+7, + lf8=Ctrl+8, lf9=Ctrl+9, mc0@, mc4=\E;`[R, mc5=\E;a[R, + nel=\r\n, op=\EG, rep=%p1%c\022%p2%'?'%+%c, rev=\E], ri=^K, + rmir=\E[4l, rmso=\E\\, rs1=\E[4l, + rs2=\024\037XA\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\n\030\014\021, + setab=\0, setaf=\E%p1%'@'%+%c, setb=\0, + setf=\E%?%p1%{1}%=%tD%e%p1%{3}%=%tF%e%p1%{4}%=%tA%e%p1%{6}%=%tC%e%p1%'@'%+%c%;, + sgr=%?%p1%t\E]%;%?%p3%t\E]%;%?%p4%t\EH%;, + sgr0=\EI\E\\\EG, smir=\E[4h, smso=\E], + tsl=\037@%p1%'A'%+%c, u6=\037%c%'A'%-%c%'A'%-, u7=\Ea, + u8=\001%[ABCPtuvwxyz0123456789:;<=>?]\004, u9=\E9{, +``` + +#### Tester : +```sh +echo $TERM +``` +##### Doit afficher : m1b +--- + +## Configuration du compte SSH + +Dans `~/.profile` de l'utilisateur SSH : + +```sh +export TERM=m1b +export LANG=C +stty -ixon icrnl onlcr -echo +``` + +### Pourquoi ? + +* `TERM=m1b` → compatibilité Minitel +* `LANG=C` → pas d'UTF-8 +* `icrnl` → ENVOI = Entrée +* `-echo` → évite les caractères en double +#### Vous pouvez choisir un autre terminal minitel visitez http://canal.chez.com/terminfo.htm +--- + +## Lancement du serveur + +```sh +python3 sshserial.py +``` + +Sortie attendue : + +```text +Rien, mias le minitel doit se connecter directement. +``` +--- + +## Problèmes connus & solutions + +### Touches en double + +Cause : double écho +Solution : `stty -echo` (déjà inclus plus haut) + +--- + +### Le caractère `@` devient `à` + +Mauvais mode de terminal sur le Minitel ( fnct T puis A ou ctrl-esc puis T puis A sur magisclub) +## Licence + +Ce projet est libre d'utilisation, de modification et de redistribution à des fins non commerciales. + +Toute utilisation commerciale est interdite sans autorisation explicite de l'auteur. + +Ce projet a été développé à des fins personnelles et éducatives, en s’inspirant de projets existants de la communauté Minitel. + +--- + +## Crédits +http://canal.chez.com/terminfo.htm + +--- + +> *"Faire dialoguer le Minitel avec l'Internet moderne."* \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..425ec9d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +paramiko>=3.4.0 +pyserial>=3.5 \ No newline at end of file diff --git a/sshserial.py b/sshserial.py new file mode 100644 index 0000000..ec4757a --- /dev/null +++ b/sshserial.py @@ -0,0 +1,69 @@ +import asyncio +import paramiko +import serial + +# --- SSH configuration --- +SSH_HOST = "ip serveur ssh" # à adapter +SSH_USER = "minitel" +SSH_PORT = 22 +SSH_PASSWORD = "password" # à adapter + +# --- Serial / Minitel configuration --- +SERIAL_TTY = "/dev/cu.usbserial-130" # à adapter (j'étais sur macOS et magis club) +SERIAL_SPEED = 9600 # ou 1200 si Minitel 1 +async def main(): + # --- Open serial port --- + ser = serial.Serial( + SERIAL_TTY, + SERIAL_SPEED, + parity=serial.PARITY_NONE, # PARITY_EVEN si Minitel 1 + bytesize=8, # serial.STOPBITS_ONE, si Minitel 1 + timeout=2 + ) + + # --- Open SSH --- + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + client.connect( + SSH_HOST, + port=SSH_PORT, + username=SSH_USER, + password=SSH_PASSWORD + ) + + chan = client.invoke_shell( + term='m1b', + width=40, + height=24 + ) + + chan.send("stty -ixon\n") + + # --- Init Minitel --- + ser.write(b'\x07\x0c\x1f\x40\x41connexion\x0a') + # disable local echo + ser.write(b'\x1b\x3b\x60\x58\x52') + + async def serial_to_ssh(): + """Minitel -> SSH""" + while True: + if ser.in_waiting > 0: + data = ser.read(ser.in_waiting) + try: + chan.send(data.decode("latin1", errors="ignore")) + except Exception: + pass + await asyncio.sleep(0.01) + + async def ssh_to_serial(): + """SSH -> Minitel""" + while True: + if chan.recv_ready(): + data = chan.recv(1024) + ser.write(data) + await asyncio.sleep(0.01) + + await asyncio.gather(serial_to_ssh(), ssh_to_serial()) + +if __name__ == "__main__": + asyncio.run(main())