Une thèse d’histoire en LaTeX : le passage au format livre

Auteur :  x0r Publié le   Nombre de commentaires : 0
Mots-clefs : latex histoire tex thèse pandoc word haskell

Après que ma bien-aimée Nausicaa ait préparé sa thèse d’histoire médiévale en LaTeX, notamment grâce à quelques trucs et astuces de LaTeX appliquées aux thèses en sciences humaines, le livre qui en est l’adaptation sera bientôt publié. Dans ce billet, qui sera la conclusion de cette petite série, je vous propose les péripéties de l’adaptation de sa thèse au format livre.

Après le long travail que représente une thèse, on serait tenté de croire que la faire publier au format livre serait une formalité. Le texte est en effet déjà prêt, alors cela ne devrait-il pas passer comme une lettre à la Poste ?

En réalité, c’est un peu plus difficile que cela. Je passe les détails sur le choix de l’éditeur : cela mériterait un billet de blog dédié au sujet. Puis, après avoir soumis la thèse telle quelle au comité de lecture, celui-ci donne un avis positif ou négatif. Mais un avis positif n’est que le début de l’aventure, car dans le cas de Nausicaa, son éditeur a demandé de réduire considérablement le texte pour ne pas dépasser les huit cent mille signes.

S’ajoute à ces contraintes une question technique de taille : la maison d’édition ne saurait que faire de fichiers LaTeX. Leur procédé de soumission impose en effet le format Word. Dans ce billet, nous verrons donc les différents obstacles techniques qu’il a fallu surmonter pour passer de la thèse au livre.

Passer de LaTeX à Word : Pandoc à la rescousse

Les formats de fichiers TeX et DOCX n’ont strictement rien à voir. TeX est en fait un langage assez bas niveau mais qu’il est possible d’enrichir avec des commandes ad hoc, jusqu’à former un mini-langage d’aide à la génération d’éléments structurels (délimiter les chapitres, les sections, etc.) et d’aide à la rédaction. C’est, du moins, ce qu’est LaTeX par rapport à TeX. DOCX, quant à lui, est une archive ZIP contenant une collection de fichiers XML, dont l’un comporte un texte dans une structure similaire au HTML ou à DocBook dans l’idée, mais bien plus complexe. J’avais déjà dit ailleurs ce que je pensais de ce format. Bref : ce sont deux mondes à part et pour des raisons techniques complexes, le passage de LaTeX à DOCX allait forcément entraîner des pertes d’information.

J’étais donc assez pessimiste quant à la fiabilité d’éventuels convertisseurs de LaTeX vers DOCX. Mais étant donné que la thèse faisait plus de six cents pages une fois imprimée et contenait au total 2 014 occurrences de \cite{} et \footcite{}, il était hors de question de faire cette conversion à la main. Il nous fallait donc impérativement un outil automatique qui sache en outre traiter les références bibliographiques.

Heureusement, après avoir essayé plusieurs outils différents, j’ai fini par trouver Pandoc qui, en plus d’être le convertisseur qui marchait le mieux, est activement développé et cochait toutes les cases de ce petit cahier des charges. Il est en outre programmé en Haskell, un langage que j’ai étudié peu de temps avant la soutenance, ce qui m’a permis de l’adapter ponctuellement à nos besoins.

Pandoc est en fait décomposé en deux parties : l’outil de conversion lui-même, la commande pandoc, auquel on peut adjoindre un autre appelé pandoc-citeproc s’il faut convertir des références bibliographiques. Pour cela, pandoc-citeproc lit une base de données bibliographique au format BibTeX et un fichier de style bibliographique au format CSL (le même format que celui utilisé par Zotero) pour produire les références dans le style souhaité.

L’architecture logicielle de pandoc me plaît bien, d’ailleurs : il est en effet conçu comme un compilateur. D’abord, pandoc utilise une front-end pour convertir le texte source dans une représentation intermédiaire, puis une back-end pour transformer cette structure intermédiaire dans le format cible. Ainsi, ajouter la prise en charge d’un format de fichiers ne nécessite l’écriture que d’une front-end et d’une back-end pour ce format et il devient alors possible de le convertir depuis ou vers n’importe quel autre format déjà pris en charge par pandoc.

Une conversion fidèle mais un peu laborieuse

J’ai donc chargé le style bibliographique des Presses Universitaires de Rennes (PUR), différent du style ISO 690 utilisé dans la thèse, et fait un essai sur un petit extrait de la thèse. J’avais utilisé les dernières versions de ces deux outils à cette époque-là : Pandoc version 2.10.1 et pandoc-citeproc version 0.17.0.2. Le résultat était plutôt convaincant, comme on peut le voir sur la comparaison que j’ai reproduite.

Comparaison des apparences visuelles de la thèse originale en LaTeX et de sa version convertie par Pandoc et pandoc-citeproc
Comparison des apparences visuelles de la thèse originale en LaTeX (en haut) et du résultat d’une conversion en DOCX par Pandoc et pandoc-citeproc, tel qu’il s’affiche dans LibreOffice Writer 7.3.7.2 (en bas). Extraits reproduits avec l’aimable autorisation de Nausicaa.

Et ainsi, j’obtenais un résultat qui, à première vue, semblait plutôt fidèle. La perte de la mise en forme lors de la conversion ne nous importait peu, mais le texte et les notes de bas de page y sont. On notera que les références bibliographiques sont également converties du style ISO 690 au style des PUR.

La conversion de la thèse complète ne s’est pas passée entièrement sans accrocs, cependant.

Premièrement, le document Word contenait des guillements à l’anglo-saxonne (“ ”), alors qu’il fallait des guillements français (« »), car Pandoc convertissait les commandes \enquote{} (du package csquotes) sans tenir compte de la langue du document. Je ne me souviens plus comment j’ai résolu ce problème, mais il me semble que j’ai dû préciser à Pandoc que le document doit être en français (avec l’option de ligne de commandes -M lang=fr-FR).

Deuxièmement, la source LaTeX était un peu trop complexe pour Pandoc. Elle est composée d’un fichier racine, these.tex, dont les réglages de forme se trouvaient dans un fichier auxiliaire these-style.sty référencé dans le fichier principal par une commande \usepackage{these-style}. Chaque chapitre se trouve dans un fichier séparé et these.tex les référence, dans le bon ordre, avec des commandes \include. Las, quand je donnais à Pandoc le fichier these.tex tel quel, la conversion échouait avec un message d’erreur vraiment pas clair :

Error at "source" (line 107, column 16):
unexpected end of input
\end{document}
               ^

La solution a donc été de préparer une version allégée de these.tex appelée these-light.tex. J’ai copié le contenu de these-style.sty directement dans these.tex avant d’en retirer pratiquement toutes les commandes de mise en forme. Parmi les \usepackage, je n’ai gardé que l’essentiel, dont biblatex, babel, csquotes, tabu, hyperref, graphicx et quelques autres. Avec cette modification, Pandoc était content.

En fin de compte, après s’être placé dans le répertoire racine de la thèse LaTeX et y avoir téléchargé le fichier CSL des PUR, j’ai fini par trouver cette ligne de commande exacte pour convertir la thèse de LaTeX vers Word :

TEXINPUTS=. pandoc \
         these-light.tex \
         --filter pandoc-citeproc \
         -M lang=fr-FR \
         -M csl=presses-universitaires-de-rennes.csl \
         -o these.docx

Un gain de temps à relativiser

À première vue, la conversion avait bien marché. In fine, seuls quelques tableaux faits avec l’environnement tabulary n’ont pas été converties correctement.

Il y avait aussi quelques problèmes dans les notes de bas de page. Par exemple, dans certaines références, il manquait des espaces entre l’abréviation « coll. » et le nom de la collection. Par ailleurs, la thèse utilisait beaucoup de raccourcis comme « ibid. » ou « op. cit. » et la moindre modification du texte a des répercussions sur ces références-là.

Or, la cible des huit cent mille signes imposée par l’éditeur imposait des coupes parfois très lourdes dans le texte. Les notes de bas de page étaient donc à reprendre intégralement quoi qu’il arrivait.

Il en émergeait aussi le besoin de compter les signes. Or LaTeX ne permet pas de facilement compter les signes d’un document fini. Une recherche sur Internet donne quelques scripts qui s’y essayent, mais je doute de leur fiabilité : avec TeX, il n’est pas possible d’accéder à une représentation intermédiaire où toutes les macros ont été substituées (afin de pouvoir, notamment, compter les caractères dans les notes de bas de page, également inclus dans le décompte). Un outil comme Pandoc serait à mon avis mieux à même d’en donner une estimation.

C’est ce besoin de surveiller constamment le nombre de signes qui a imposé le workflow consistant à convertir la thèse au format DOCX d’abord et la réduire ensuite. Sinon, le travail aurait probablement été fait dans l’ordre inverse : réduire le texte en intervenant sur la source LaTeX d’abord, puis convertir en DOCX ensuite.

Dans tous les cas, après une conversion comme celle-ci, il est indispensable d’examiner intégralement le résultat afin de s’assurer que rien n’a été perdu au moment de la conversion. Pour cette raison, convertir d’abord et réduire ensuite était peut-être la bonne méthode.

Conclusion : Markdown comme format source ?

Les éditeurs qui imposent des formats de fichiers qui, sans l’existence d’alternatives libres, auraient imposé l’achat d’un logiciel onéreux et non libre, sont un effet émergent regrettable de la mainmise qu’ont certains éditeurs de logiciels sur le marché des suites bureautiques, alors qu’il existe des systèmes de préparation mieux adaptés aux travaux scientifiques, mais moins connus en dehors des milieux techniques.

Or, pour des publications scientifiques préparées sur ordinateur, il est justement important de pouvoir conserver les textes dans des formats de fichiers pérennes et peu susceptibles de tomber dans l’obsolescence. Les formats reposant sur du texte brut, combiné à des outils externes transformant ce texte brut en un format lisible, ont donc tout intérêt à être favorisés.

Pandoc pourrait peut-être bien apporter une solution à cela : en permettant à l’auteur de rédiger son texte dans l’outil de son choix puis de convertir à la demande dans les formats requis par les éditeurs, nul besoin d’enfermer son propos dans des formats difficiles à lire par autre chose que le logiciel de l’éditeur. Pour ce faire, Pandoc propose une variante de Markdown avec des extensions propres à Pandoc, conçu pour que tout document préparé dans ce format puisse être converti sans trop de pertes dans l’un des autres formats cible de Pandoc – y compris LaTeX.

Pour ces raisons, si je devais moi-même faire une thèse, j’envisagerais sérieusement de rédiger le manuscrit en Markdown-Pandoc, puis de passer par une conversion vers LaTeX pour en faire un joli PDF. Le résultat sera toujours aussi esthétiquement plaisant grâce à la puissance de TeX, mais cela me donne davantage de souplesse vis-à-vis des exigences des éditeurs lorsqu’il sera temps de la publier. La méthodologie est décrite dans l’article de Dennis Tenen et Grant Wythoff intitulé Sustainable Authorship in Plain Text using Pandoc and Markdown, qui montre qu’en sciences humaines tout du moins, cette approche pourrait bien être le meilleur de plusieurs mondes.

Délégation de préfixes IPv6 avec la Livebox : expériences en profondeur

Auteur :  x0r Publié le   Nombre de commentaires : 20
Mots-clefs : livebox ipv6 dhcpv6-pd

Mise à jour du 7 octobre 2024 : il ne suffit pas d’avoir des adresses MAC différentes pour chaque bail ; il faut envoyer un DUID différent aussi…

Mise à jour du 7 août 2022 : la version 4.53.14 du firmware souffre des mêmes problèmes.

Un soir, j’ai été surpris de découvrir que la Livebox propose désormais une fonction de délégation de préfixes IPv6 dans son interface d’administration.

Capture d’écran de l’interface d’administration de la Livebox, montrant la nouvelle fonctionnalité appelée « Routage IPv6 »

C’est une fonction que j’attendais depuis très longtemps sur la Livebox, car sans elle, je ne pouvais pas avoir de connectivité IPv6 à cause de la configuration particulière de mon réseau. Au lieu de me fier directement à ma Livebox pour le routage, le pare-feu et le Wi-Fi, j’ai en effet choisi d’utiliser mes propres équipements au lieu de la box, et je n’utilise cette box que comme un pont entre le réseau délimité par mon propre routeur et le réseau d’Orange.

Après quelques heures passées à expérimenter, j’ai fini par être un peu déçu à cause de quelques limitations, selon moi injustifiées, imposées par la Livebox. Dans ce billet, je vous propose d’approfondir ce que j’ai déjà écrit sur Mastodon, avec quelques exemples, captures d’écran et captures réseau à l’appui. J’ai effectué mes tests sur une Livebox 4 ; les premiers ont été effectués avec la version 4.43.12 du firmware et des tests complémentaires pour préparer cet article avec la version 4.46.0, ma box ayant été subrepticement mise à jour (mais apparemment, les bugs seraient les mêmes).

La délégation de préfixes par DHCPv6

Le mécanisme de délégation de préfixes utilise une option DHCPv6, appelée IA_PD, spécifiée dans la RFC 3633. Un routeur souhaitant obtenir une délégation de préfixe (appelé requesting router dans la RFC, mais j’utiliserai ici le terme « routeur délégataire ») envoie un paquet DHCPv6 Solicit contenant, dans une ou plusieurs associations d’identité (IA), une option IA_PD. Le routeur délégant (delegating router) répond alors avec un DHCPv6 Advertise contenant elle aussi un ou plusieurs IA_PD par préfixe qu’il accepte de déléguer.

Orange utilise déjà la délégation de préfixes IPv6 dans son propre réseau pour attribuer les préfixes IPv6, des /56, aux abonnés. Donc si un routeur en aval de la Livebox peut demander des délégations de préfixes à la Livebox, on obtient une chaîne de délégation comme ceci :

  • pour le préfixe 2001:db8:af3c:5a00::/56, un routeur quelque part dans le réseau opérateur d’Orange est le routeur délégant et la Livebox est le routeur délégataire ;
  • pour le préfixe 2001:db8:af3c:5a82::/64, la Livebox est le routeur délégant et le routeur aval est le routeur délégataire.

Schéma illustrant la chaîne de délégation de préfixes IPv6 dans le cas d’un réseau où un routeur se trouve en aval de la Livebox.

Position du problème

La force des choses, c’est-à-dire, entre autres, ma passion pour l’informatique, ma méfiance vis-à-vis de certains appareils connectés que je présume être trop bavards, une certaine pandémie mais surtout une simple question d’hygiène informatique, m’a amené à découper mon réseau domestique en plusieurs VLAN : bureautique, multimédia (et autres bidules connectés), consoles de jeux, téléphonie sur IP, administration, labo d’expérimentation IPv6 et télétravail. Tous ces VLAN sont isolés entre eux et certains sont également isolés d’Internet.

En IPv4, parmi les plages d’adresses mises de côté par la RFC 1918 pour un usage privé, j’ai choisi un /16 que je découpe ensuite en /24 pour chacun de mes VLAN. En IPv6, il me faut idéalement un /64 par VLAN, donc le fait qu’Orange alloue un /56 par abonné est plutôt opportun. Bien que la Livebox réserve un des 256 /64 disponibles, cela me laisse tout de même la possibilité d’adresser 255 VLAN en IPv6, ce qui suffira amplement pour mes besoins.

Plus tôt, quand j’avais mis en place ma connectivité IPv6 en étant chez Free, j’avais un réseau beaucoup plus simple dans lequel je n’avais qu’un seul VLAN ; j’utilisais d’ailleurs un démon, ndppd, pour jouer le rôle de proxy NDP. Mais je souhaite écarter d’emblée cette solution, que je considère comme un honteux bidouillage qui oblige, par ailleurs, à utiliser des préfixes plus longs dans ses VLAN comme des /80, ce qui impose la mise en œuvre d’un serveur DHCPv6.

Les limitations de la Livebox en matière de délégation de préfixes

En passant une soirée à expérimenter avec la Livebox, j’ai pu trouver trois problèmes majeurs qui font obstacle, dans l’immédiat, à un déploiement généralisé d’IPv6 dans mon réseau : l’impossibilité de demander la délégation d’un préfixe plus court qu’un /64 ; l’impossibilité de choisir à l’avance le /64 à déléguer ; enfin, l’incapacité, pour la box, de déléguer correctement plus d’un /64 au même routeur délégataire lorsque ce dernier en demande plusieurs.

Déléguer un /56 ou un /60 ne fonctionne pas

Mon premier essai consistait à demander la délégation de mon /56 complet, c’est-à-dire 2001:db8:af3c:5a00::/56 dans le schéma d’exemple. Au lieu de cela, la Livebox me délègue un /64 que je n’ai pas choisi !

On le voit d’ailleurs bien dans l’interface d’administration de la Livebox : l’adresse MAC de mon routeur apparaît bien, mais pas le /56 que j’avais demandé. Sur le routeur délégataire, aucune des interfaces qui devait profiter des préfixes délégués par la Livebox n’a d’adresse IPv6, faute d’obtenir la délégation souhaitée. Donc demander un /56 ne marche pas.

Capture d’écran de l’interface d’administration, montrant que le préfixe
  2a01:cb04:743:8dc5::/64 a été délégué à l’équipement « gw », dont l’adresse
  MAC est 80:2A:A8:4C:39:66.

À première vue, il est assez logique qu’une demande de délégation de 2001:db8:af3c:5a00::/56 n’aboutisse pas : ce /56 correspond à la plage complète déléguée à la Livebox, or elle réserve déjà 2001:db8:af3c:5a00::/64.

Mais même demander un /60 ne fonctionne pas non plus : dans ce cas, la Livebox délègue obstinément un /64, au lieu de renvoyer une erreur. Il y a pourtant 15 préfixes /60 qui n’entrent pas en conflit avec le /64 réservé par la Livebox.

Rien n’empêche a priori la Livebox d’accepter de déléguer 2001:db8:af3c:5a00::/56 excepté 2001:db8:af3c:5a00::/64 : il est même possible d’exprimer cela grâce à une option DHCPv6 décrite dans la RFC 6603. Le routeur délégataire doit pour cela signaler la prise en charge de cette RFC dans son DHCPv6 Solicit. Or le client wide-dhcpv6, utilisé par mon routeur, ne le fait pas ; seul dhcpcd sait le faire.

J’ai donc cherché à savoir si la Livebox prend en charge cette option. Pour ce faire, j’ai pris une machine sous Linux, installé dhcpcd dessus, créé une interface virtuelle avec la commande ip link create dummy0 type dummy, puis préparé un fichier de configuration dhcpcd.conf avec le contenu suivant :

interface eth0 
  ia_pd 0/::/56 eth0/0 dummy0/1

J’ai alors lancé dhcpcd -6B -f /chemin/vers/dhcpcd.conf eth0, tout en étant connecté en Ethernet à ma Livebox, pendant que je capturais les paquets DHCPv6. Malgré la présence de l’indication de prise en charge de la RFC 6603 dans le DHCPv6 Solicit, la Livebox continue d’attribuer un /64 aléatoire.

C’est d’autant plus dommage que le souhait de disposer librement des sous-préfixes 2001:db8:a3fc:5a01::/64 à 2001:db8:a3fc:5aff::/64 est à mon avis le cas d’usage le plus répandu nécessitant la délégation de préfixes par la Livebox. Même disposer seulement d’un /60 m’aurait déjà permis de connecter en IPv6 seize VLAN, ce qui aurait été largement suffisant pour mon usage.

En l’état, cette mise en œuvre de la délégation de préfixes par la Livebox est déjà une opportunité manquée.

Pas de choix du préfixe délégué

Nous savons maintenant que la Livebox n’accepte de déléguer que des /64.

Sur l’interface d’administration de la box, il n’est pas possible de configurer à l’avance quels sous-préfixes la box peut déléguer : au lieu de cela, à chaque fois qu’un routeur délégataire demande un /64, il s’en voit attribuer un aléatoire par la box.

Ces préfixes ont l’air stables, c’est-à-dire que le même couple DUID/IAID donne le même préfixe ; mais attention, si on désactive puis réactive la fonction de délégation de préfixes sur la Livebox, tous les préfixes anciennement attribués sont oubliés et la box attribuera d’autres préfixes choisis au hasard pour le même couple DUID/IAID. Ou du moins, c’est ce qui est arrivé une fois : lorsque j’ai essayé de reproduire ce comportement, je n’y suis pas arrivé et la Livebox a attribué au même couple DUID/IAID le même /64. Quoi qu’il en soit, il manque cruellement d’options pour paramétrer plus finement les préfixes délégués.

Le caractère aléatoire des préfixes délégué n’est guère pratique quand on souhaite placer des serveurs dans une plage d’adresses ainsi déléguée : un préfixe imprévisible complique la publication de leurs adresses dans le DNS.

Pourtant, il est possible pour un routeur délégataire de demander la délégation d’un préfixe bien précis. Mais même quand l’option IA_PD du message DHCPv6 Solicit émis par le routeur délégataire désigne un préfixe qu’il souhaite obtenir, la Livebox n’en tient pas compte.

Au plus un /64 pour un équipement

Nous savons maintenant que la Livebox ne délègue que des /64 et qu’il est impossible de savoir à l’avance quels sont les préfixes qu’il distribue à chaque équipement. Au moins, elles devraient être relativement stables.

Dans un message DHCPv6, il est possible de demander la délégation de plusieurs préfixes, chacun identifiés par des IAID uniques (un IAID étant un entier non signé de 32 bits). Il serait donc normalement possible de contourner les précédentes limitations en demandant autant de délégations distinctes que de VLAN dans mon réseau.

J’ai donc essayé d’envoyer à la Livebox un DHCPv6 Solicit avec deux délégations, ayant les IAID respectifs 0 et 1. À première vue, ça a l’air de fonctionner, car je reçois bien deux préfixes /64 dans le DHCPv6 Advertise avec lequel la Livebox me répond.

Mais je suis alors tombé sur un autre bug de la Livebox : en passant d’un seul préfixe avec l’IAID 0 à deux préfixes avec les IAID 0 et 1, celui associé à l’IAID 0 perd toute connectivité à Internet (du moins, il ne m’est plus possible de pinger une autre machine en IPv6 avec) mais l’autre fonctionne.

En somme, quand un routeur délégataire demande plusieurs délégations de préfixes, toutes sont accordées, mais seule la dernière apparaissant dans le message DHCPv6 Solicit est routable. Impossible, donc, de faire router sept /64 en IPv6 par la Livebox vers mon routeur !

La fonction a-t-elle été testée ?

Je note aussi qu’il est impossible de désactiver la fonction. Après avoir décoché la case « Activer la délégation de préfixe IPv6 » puis cliqué sur « Enregistrer », j’avais fait un test en envoyant un DHCPv6 Solicit avec l’option IA_PD : la box répond quand même avec un IA_PD dans son DHCPv6 Advertise.

Ensuite, comme on peut le voir sur les captures d’écran plus haut dans ce billet, il semble que les informations affichées sur la page sont incomplètes : deux champs auraient dû afficher des informations d’adressage et de préfixes, mais ceux-ci n’apparaissent pas.

Conclusion

En l’état, la fonction de délégation de préfixes IPv6 telle que faite par la Livebox est un pas vers la bonne direction, mais sa mise en œuvre est encore loin d’être satisfaisante et ne me permet pas encore de relier mon réseau domestique à IPv6.

On dirait que la fonction a été développée à la va-vite, avec pour objectif de cocher une case sur une plaquette marketing avant tout, et sans tenir réellement compte des besoins et des cas d’utilisation de ce genre de fonctions. Le résultat est donc une bonne idée, mais insuffisamment testée, mal exécutée et à côté de la plaque. Dommage, parce que j’apprécie beaucoup qu’Orange ait la générosité de donner un /56 à chaque abonné !

Si je devais concevoir une fonction similaire sur une box d’opérateur, j’aurais prévu une interface de configuration permettant de lister précisément, pour un DUID donné, quels sont les préfixes à déléguer, IAID par IAID, avec une taille allant jusqu’au /56 (et si l’utilisateur sélectionne le /56, ce sera fait à l’exception du /64 réservé par la box, bien entendu). Cela permettrait de contrôler quels sont les routeurs ayant le droit de demander des délégations au sein de son réseau, mais aussi de savoir à l’avance qui obtient quoi. Ce serait par ailleurs plus simple à mettre en œuvre d’un point de vue technique, tout en étant moins susceptible d’avoir des bugs. Enfin, je pense que procéder ainsi couvrirait tous les cas d’usage possible d’une délégation de préfixes IPv6.

Bien sûr, d’aucuns me diront qu’il y a moult instructions sur des forums comme lafibre.info qui expliquent comment se passer de la Livebox. Mais c’est oublier que substituer un routeur tiers à la Livebox revient à mettre une croix sur la ligne téléphonique fixe, incluse dans l’abonnement et que j’utilise beaucoup ; et puisque j’ai déjà eu des problèmes quand j’ai été un peu trop curieux à propos de la téléphonie, ce n’est pas pour tout de suite. En attendant, il n’y a plus qu’à croiser les doigts pour qu’un jour, ces bugs soient corrigés.

Annexes

Captures réseau

Générer des certificats d’authenticité d’œuvres d’art avec LaTeX et GPG

Auteur :  x0r Publié le   Nombre de commentaires : 1
Mots-clefs : photo gpg cryptographie art vente

J’ai récemment eu l’occasion de vendre une de mes photos, dont j’avais fait tirer une petite série de cinq exemplaires numérotés et signés dans un laboratoire spécialisé en tirages d’art. Je ne l’avais pas vraiment prévu, car ça s’était passé au détour d’une conversation.

Cette vente était une petite source d’angoisse, car quand on vend une photo, il convient de fournir à l’acheteur un certificat d’authenticité, prouvant l’origine (et l’originalité) de l’œuvre achetée. Ce que je n’avais jamais fait avant.

Ces certificats d’authenticité sont généralement édités sur des papiers spéciaux, avec des encres spéciales, accompagnés de filigranes, hologrammes et autres mesures de sécurité pour en dissuader la falsification. Mais je n’avais pas du tout le temps pour faire éditer un certificat sous cette forme-là ; je n’avais qu’une soirée pour fournir à mon acheteur quelque chose de semblable. Heureusement, puisqu’il a des notions solides en cybersécurité, je savais que je pouvais improviser quelque chose avec rien d’autre que LaTeX, GPG, qrencode et ImageMagick.

Mais alors, comment créer ce certificat ?

En guise d’exemple, je vais utiliser la photo ci-dessous, que j’ai prise récemment :

Photo de TGV Duplex
TGV 2N2-3UF (rame 820), assurant le train 5316 (Le Havre – Marseille), photographiée le 15 février 2022.

Imaginons que j’aie fait tirer cette photo à dix exemplaires par mon laboratoire de prédilection, puis que j’aie vendu l’exemplaire numéro 1 de cette série. Je vais donc préparer un certificat d’authenticité qui reprend les informations ci-dessous, que je sauvegarde par ailleurs dans un fichier texte (appelons-le meta.txt) :

Auteur: x0r
Titre: TGV 2N2-3UF (rame 820)
Date de la prise de vue: 15 février 2022
Numéro du tirage: 1/10
Format: 45 × 30 cm
Type d'impression: Piezography - Encres pigmentaires Epson UltraChrome™ HDR
Papier: Platine Fibre Rag 310 g/m² Canson

C’est une bonne idée d’ajouter, sur le certificat, une petite vignette de l’image en question. Je la prépare donc avec une résolution suffisante pour apparaître à environ 300 dpi sur la feuille :

% convert photo -quality 80 -resize 1000x thumb-photo.jpg

Avec tout ça, on a de quoi faire un certificat d’authenticité avec LaTeX ; je pourrais alors l’imprimer, la signer et m’arrêter là. Mais n’importe qui pourrait le falsifier, alors que faire ?

C’est là où GPG peut nous aider. Tout d’abord, je signe mon fichier meta.txt avec GPG :

% gpg --clear-sign meta.txt

J’utilise l’option --clear-sign afin de conserver le contenu de meta.txt lisible. Le fichier signé, meta.txt.asc, ressemble à ceci :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Auteur: x0r
Titre: TGV 2N2-3UF (rame 820)
Date de la prise de vue: 15 février 2022
Numéro du tirage: 1/10
Format: 45 × 30 cm
Type d'impression: Piezography - Encres pigmentaires Epson UltraChrome™ HDR
Papier: Platine Fibre Rag 310 g/m² Canson
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEE/JoAQPj3zjdYk5ClL0gWKnq2LYYFAmJ2ZvQACgkQL0gWKnq2
LYZ0RBAAwYLAjfHcz26d9FTJemxWGattypV6m7BReH9n9XDUSP8TR0n0fjgw5sf5
meoddDc5SyXZW8O9wZVYZygnFiZIo98TQCCkWfWgLc2wOPMNKZAT7s1c6kgcFIrU
wldKq0udGtF5psRYWd6pLjgjNFMzB8lk+SPM6zi8pS/9pJcNqrYTCTbB+Pdbbrm2
NJcLwIn6gCRNUtz51dLtWywszej9nmY1E00QqTzBaFzxisGX1S64jiOawprYcNHr
ysrUzKvWTg8ywC9h9g47lP5ZkN++ak87y9iqkojwfky5aVdsfC/S14wB5vADmUEg
9jIbVbG2YWaA9bFPvgw0vCPm/qLGhmQ9ARTHMrLJj6uBkoQQTh98EBvp0oyA38c0
qJNZSt6W5bD/MJyZsZ9HY/yRzpswNa0KLzYH5dFQ2QhmkhnFHJ6VwZTGMNefZFDn
qz058EAWDM3rq7beLz0ksAjImOn8s4dr3k7kA0ydUrqRANZsSl0cUlwKKn5j412v
YuvjCud9oBFtczFbz4K21VKrDlTPRL2q2rQpsX1UZyLezm4MfJTmYd1IEEJrkC/F
6t5xbnlcomAshQK9+3XI96Jog4CMTcSGZwux1VasbsCWmONh9aE6CRmxB3CZuD6x
NMNGcSHVnhH56hjlP/RYv5X9IzRoChh3e4+nvDQVYLRNweoRldk=
=yE6O
-----END PGP SIGNATURE-----

Puis, on en fait un code QR.

% qrencode -r meta.txt.asc -o meta_qr.png

L’ajout de la signature cryptographique multiplie par plus de quatre la taille de l’entrée : on passe de 249 à 1 131 octets. Mais on reste largement sous la limite permise par la spécification, car le résultat est un code QR version 24, de 113 × 113 pixels, alors que les codes QR les plus grands, appelés « version 40 », ont pour dimensions 177 × 177, soit presque 2,5 fois la surface d’une version 24.

Comme j’utilise XeLaTeX, il faut d’abord convertir cette image au format PDF :

% convert meta_qr.png meta_qr.pdf

Et ensuite, on peut l’embarquer dans le document. J’y ajoute un paragraphe décrivant l’utilité de cet énorme QR code, ainsi que l’empreinte de la clef publique ayant permis de signer les métadonnées.

Certificat d’authenticité avec un QR code

Et voilà le résultat : un joli document avec toutes les informations à propos de ma photo vendue, avec une signature électronique qui protège contre la majorité des fraudes. Une fois imprimée, j’y ajoute juste une signature de ma propre main, en utilisant une encre indélébile.

En pratique, le code QR a besoin de faire au moins 6,4 cm de côté pour qu’il puisse être lu par un téléphone. Avec une imprimante à jet d’encre, les points du QR code peuvent donc avoir tendance à « baver » ; descendre en-dessous de cette taille risque de rendre ce code QR inexploitable.

Vérification de la signature

Une signature électronique n’aurait que peu d’intérêt s’il n’y avait aucune façon de la vérifier.

Avec un téléphone Android, c’est facile : il faut d’abord installer une application de scan de codes QR (j’utilise QR Scanner de SECUSO Research Group, mais si vous en connaissez d’autres, sans publicité, qui sont recommandables, je suis tout ouïe), puis OpenKeychain pour gérer la partie GPG.

Puis il faut importer ma clef publique dans l’application OpenKeychain (il n’est pas forcément nécessaire de créer des clefs GPG).

Ensuite, pour vérifier ma signature numérique, il faut procéder ainsi : scannez le QR code, sélectionnez l’icône « Partager », puis l’icône OpenKeyChain avec la légende « Déchiffrer… ». Vous devriez alors voir à l’écran l’indication que la signature est bonne.

Après avoir scanné le code QR, on peut vérifier que la signature est bonne.

Que se passe-t-il si j’essaye de falsifier le contenu du QR Code ? Ici, j’ai changé le numéro du tirage de 1/10 à 1/30 dans meta.txt.asc, puis j’ai généré un nouveau code QR. Lorsque je le scanne, le changement est visible dans le texte en clair… mais OpenKeychain m’affiche un gros avertissement, indiquant que quelqu’un a modifié le texte.

Mais si je modifie ne serait-ce qu’un caractère, la signature n’est plus valable.

Conclusion

Vous savez maintenant comment j’ai improvisé un certificat d’authenticité pour des tirages d’art que je ne pensais pas forcément vendre de façon organisée.

Bien entendu, si j’avais vraiment voulu vendre mes photos de façon habituelle (autrement dit, si j’avais pris le statut d’artiste-photographe par exemple), je me serais tourné vers des certificats d’authenticité édité par des services visant un public de photographes professionnels. Pour les acheteurs, vérifier un document comme celui-ci nécessite tout de même d’être familiarisé avec GPG, une compétence que je ne pourrais pas exiger d’acheteurs « lambda ».

Au moins, la seule façon réaliste de frauder serait de générer un document similaire, mais de tout signer avec une clef qui ne serait pas la mienne. Seule une personne avertie qui connaîtrait préalablement l’empreinte de ma vraie clef publique pourrait alors déceler le subterfuge. Mais cette méthode me donne, pour mon cas d’utilisation, un niveau de protection qui me semble tout à fait acceptable.

En fabriquant ces certificats d’authenticité, j’ai beaucoup pensé aux non-fungible tokens (NFT), la nouvelle mode du moment (et bien expliqués dans cet article). Je ne me suis pas intéressé de plus près à ce nouvel objet de spéculation, mais pourtant, la question se pose : aurais-je créé une sorte de « NFT physique » sans le savoir ?

Le contenu du fichier meta.txt.asc tout seul ne suffirait pas pour qualifier de NFT : j’y ai par exemple omis une empreinte (SHA-512 par exemple) du fichier JPEG que j’ai fait tirer, car la miniature figurant sur le certificat joue déjà ce rôle. Ce n’est donc pas meta.txt.asc qui jouerait le rôle de « NFT physique », mais bel et bien le document imprimé complet. Même si là encore, ça se discute : cette feuille de papier ne rentrera dans aucune chaîne de bloc et je ne peux pas non plus y attacher un contrat automatique stipulant que je touche de l’argent à chaque revente. Mais là, comme ça, c’est déjà pas mal.

Liens externes

Trois applications d’Asterisk à la maison

Auteur :  x0r Publié le   Nombre de commentaires : 0
Mots-clefs : sip asterisk

Je dois faire partie d’une infime minorité de gens qui, à mon âge, utilise le téléphone fixe et je dois bien faire partie d’une minorité encore plus infime en utilisant cette ligne fixe avec un système Asterisk à la maison.

Ce que j’avais à l’origine installé chez moi pour jouer s’est révélé être de plus en plus utile avec le temps. D’abord parce que feu mon projet siproxd_orange a été une excellente façon d’apprendre le SIP. Mais ensuite parce que mon infrastructure existante m’a permis de développer de petites applications pas forcément très compliquées, mais très utiles. Autrement dit, mon serveur Asterisk est passé du statut de solution en quête de problèmes à une solution pour de vrais besoins que j’ai fini par avoir plus tard.

J’ai pu en effet mettre en œuvre récemment quelques idées d’applications utiles et, de surcroît, appréciées par ma bien-aimée. Ces applications ont pour point commun d’exploiter une fonction que proposent certains téléphones SIP : le décroché automatique sur haut-parleur pour certains appels entrants. Dans ce billet, je vous en propose trois : un simple système d’interphone, un système de diffusion globales d’annonces vocales et un système de rappels automatique par diffusion globale.

Avant de commencer, je tiens à préciser que j’utilise PJSIP en faveur de l’ancien module chan_sip que je ne compile même plus dans mes propres installations d’Asterisk et qui est officiellement déprécié depuis Asterisk 19. Si vous utilisez encore chan_sip, à vous d’adapter mes exemples.

Interphone

Imaginons le scénario suivant : je viens de finir ma cuisine pendant que Madame est sur son PC en train de jouer. Pour l’appeler à table, je pourrais crier à travers l’appartement « À table ! », mais je n’ai pas envie de m’égosiller à chaque fois. Je pourrais aussi composer, depuis le téléphone de la cuisine, le numéro du poste à son bureau : mais alors, comme elle a les deux mains occupées, elle ne peut pas décrocher.

La solution serait ce que j’appellerai ici « interphone » par souci de concision : c’est-à-dire la possibilité pour moi d’appeler un poste et que cet appel soit automatiquement décroché sur haut-parleur.

Certains modèles de téléphones SIP déclenchent une prise automatique d’appel sur haut-parleur si un en-tête particulier se trouve dans le message INVITE, en général Alert-Info, et a une valeur particulière. En tout cas :

  • sur des Yealink W52P, il faut un Alert-Info égal à Auto answer ;
  • sur des Polycom SoundPoint IP 450, la valeur nécessaire est configurable, alors j’ai fait en sorte de s’aligner sur les Yealink.

Pour d’autres constructeurs, il y a peut-être besoin d’une valeur différente, d’un en-tête différente ou alors la fonction n’existe pas du tout.

Pour ajouter cet en-tête dans le message INVITE envoyé à l’appelé, il faut pouvoir positionner la variable PJSIP_HEADER(add,Alert-Info) du demi-appel (call leg) entre Asterisk et l’appelé. Le faire directement dans l’extension (i.e. faire un Set juste avant le Dial vers le destinataire) ne marchera pas, car cela agit sur la signalisation SIP entre l’appelant et Asterisk.

Il faut donc passer par une astuce de l’application Dial : l’option b permet d’indiquer vers quelle extension effectuer un Gosub dans le contexte du demi-appel entre Asterisk et l’appelé.

J’ai créé un contexte handlers, dans lequel je place des extensions qui ne permettent pas d’appeler des téléphones. L’extension dont j’ai besoin, que j’ai appelée set_auto_answer, est définie ainsi :

[handlers]

exten => set_auto_answer,1,NoOp()
 same => n,Set(PJSIP_HEADER(add,Alert-Info)=Auto Answer)
 same => n,Return()

Enfin, j’ai choisi de mettre en œuvre l’accès à ce service en préfixant le numéro du poste par *. Par exemple, 4042 déclenche un appel classique mais *4042 donne un appel d’interphone. Il ne reste plus qu’à déclarer l’extension qui me permet d’appeler un poste ainsi, comme ceci :

[phones]

exten => *4042,1,NoOp()
 same => n,Dial(PJSIP/foo,b(handlers^set_auto_answer^1),3)
 same => n,Hangup()

Le paramètre crucial ici étant l’option b(…) donnée à l’application Dial. Avec ceci, on devrait désormais avoir un interphone fonctionnel.

Une limitation de ce système que je trouve un peu frustrante, c’est que je n’ai pas d’indication claire du moment exact à partir duquel je peux parler. C’est d’autant plus problématique quand j’appelle depuis ou vers un téléphone Yealink, parce qu’ils sont lents.

Diffusion globale

Maintenant qu’on a une fonction d’interphone, on peut s’en servir pour en faire un système de diffusion globale, aussi appelée « paging » en anglais.

Cela consiste à définir un numéro qui, une fois composé, permet de diffuser une annonce audible dans un bâtiment entier (par exemple, dans un magasin) en parlant dans le combiné du téléphone.

Pour cela, il faut utiliser l’application Page au lieu de Dial, car Page crée une conférence ad hoc dans laquelle sont placés tous les téléphones appelés, alors que Dial aurait simplement provoqué la diffusion sur un téléphone au hasard. Les options sont très proches. J’ai choisi l’extension *4000 pour qu’il soit facile à mémoriser. Dans extensions.conf, voilà ce que ça donne :

[globals]

; Liste de tous les téléphones prenant en charge le décroché auto
PAGE_ALL_PHONES => PJSIP/foo&PJSIP/bar&PJSIP/baz&…

[phones]

exten => *4000,1,NoOp()
 same *> n,Page(${PAGE_ALL_PHONES},b(handlers^set_auto_answer^1)di,3)
 same => n,Hangup()

Je laisse trois secondes à tous les téléphones pour prendre l’appel. Ce délai permet de gérer correctement le cas d’un téléphone déjà en communication : dans ce cas, le téléphone sonne en double appel, mais cet appel est abandonné après quelques secondes chez ceux qui n’ont pas pu le prendre directement sur haut-parleur. J’ai procédé par tâtonnements pour trouver le délai idéal, surtout à cause de mes Yealink, qui mettent beaucoup de temps avant de réagir à un message INVITE. Un délai de 2 s s’est avéré insuffisant, par exemple, mais 3 s semble être plus fiable.

Je me donne la possibilité d’avoir du son bidirectionnel avec l’option d. Avec cette option, Asterisk mixe ensemble le son capté par l’ensemble des téléphones appelés et envoie le résultat à l’appelant, ce qui permet à l’appelé, où qu’il soit, de répondre comme pour un appel interphone classique. Attention cependant à l’effet Larsen quand on teste avec plusieurs combinés proches les uns des autres ou quand plusieurs postes se trouvent dans la même pièce !

Le résultat ne me permet que des annonces en direct. On pourrait imaginer une version plus avancée qui permette d’enregistrer un message, de le réécouter puis de diffuser ce message après validation, comme ce que permettent certains systèmes commerciaux, mais je n’ai jusqu’ici pas eu besoin d’une application aussi complexe.

Rappels automatiques

Des circonstances particulières font que ma bien-aimée a ressenti le besoin d’avoir un système de rappels à des heures fixes. Comme elle n’a pas et ne veut pas de smartphone, je me suis dit que je pouvais rapidement bricoler un système utilisant le service de diffusion globale que j’avais déjà avant : comme ça, au moins, je suis certain que le message atteindra sa cible à l’heure souhaitée.

Ma solution est mise en œuvre par une tâche cron, qui exécute un script shell, qui dépose dans le répertoire /var/spool/asterisk/outgoing un fichier d’appel (call file) spécial. Ce fichier décrit un appel émis par un pseudo-canal local vers le service de diffusion générale que j’avais déjà.

Tout se passe dans un contexte dédié que j’ai appelé reminder et qui se programme de la façon suivante :

[reminder]

exten => broadcast,1,NoOp()
 same => n,Page(${PAGE_ALL_PHONES},b(handlers^set_auto_answer^1)iq,3)

exten => play_message,1,NoOp()
 same => n,Answer()
 same => n,Wait(2)
 same => n,Playback(reminder)
 same => n,Hangup()

L’application Playback joue un fichier appelé reminder, préalablement enregistré par mes soins et placé dans le répertoire des sons d’Asterisk.

Ensuite, le fichier d’appel en question a le contenu suivant :

Channel: Local/play_message@reminder
Context: reminder
Extension: broadcast 
Callerid: Rappel automatique <reminder>

La variable Channel d’un fichier d’appel indique normalement le poste qu’Asterisk fait préalablement sonner. Une fois décroché, ce canal est alors envoyé vers le triplet Content, Extension et Priority donné en argument (et Priority vaut 1 par défaut). Dans notre cas, on utilise un canal « local », qui simule ici une personne ayant appelé le service de diffusion globale, attend deux secondes (pour laisser le temps aux Yealink de réagir) et qui parle dans son combiné avant de raccrocher.

Enfin, le script appelé par la tâche cron est assez simple. Une façon de faire (qui nécessiterait les droits root, donc l’idéal serait que ce soit fait sous un autre utilisateur non privilégié, éventuellement avec des droits sudo) serait de procéder ainsi :

install -m 600 -o asterisk -g asterisk \
        /chemin/vers/reminder.call \
        /var/spool/asterisk/_tmp_call && \
    mv /var/spool/asterisk/_tmp_call \
       /var/spool/asterisk/outgoing/reminder.call

L’écriture de ce genre de scripts exige une attention particulière car il y un piège : il y a en effet un risque de race condition entre le processus qui écrit le fichier d’appel et Asterisk qui traite un fichier d’appel dès qu’il apparaît dans le répertoire adéquat. Si on ne fait pas attention, Asterisk risque de traiter un fichier tronqué, vierge ou illisible pour cause de droits insuffisants et l’appel échouera. Pour éviter cela, il faut procéder en deux étapes :

  • écrire le fichier dans un répertoire proche, changer le propriétaire et le groupe en asterisk et le mode en 600 (pas besoin de plus) ;
  • quand le fichier est prêt, le déplacer (mv) dans le bon répertoire ; le déplacement d’un fichier étant atomique tant que le répertoire source et destination sont sur le même système de fichiers.

Ce système de rappels automatiques a ainsi fonctionné pendant plus d’un mois sans faillir (excepté au début, lors de sa mise au point, à cause du piège mentionné ci-dessus notamment).

Conclusion

J’ai montré comment construire pas à pas une petite gamme d’applications reposant sur le principe d’appels internes automatiquement pris sur haut-parleur.

La fonction d’interphone est très appréciée par ma compagne, parce qu’elle apporte une alternative au fait de hurler « C’est prêt ! » quand j’ai fini de faire la cuisine qui lui est beaucoup moins désagréable. La diffusion globale lui permet ensuite de n’avoir qu’un seul numéro à mémoriser. Enfin, le rappel périodique en diffusion globale lui a été très utile pendant plus d’un mois et lui sera encore d’une aide très précieuse pour les quelques mois à venir. Voilà donc un exemple concret d’une application intéressante pour un PBX à la maison !

Mais ce n’est pas tout : la souplesse de mon serveur Asterisk, et plus largement le fait d’avoir une infrastructure réseau chez moi qui ressemble peu ou prou à un réseau d’entreprise, m’ont bien servi pendant les épisodes de confinement dues à la pandémie du Covid-19. Mais ceci sera un sujet pour un autre billet de blog.

Des changements dans les coulisses de monrer.fr

Auteur :  x0r Publié le   Nombre de commentaires : 20
Mots-clefs : sncf gtfs ratp monrer.fr postgresql

Mise à jour du 14 août 2021 : la migration s’est déroulée sans encombres. Merci pour votre patience !

Mise à jour du 9 août 2021 : ajout d’un paragraphe sur les numéros des trains du RER D (sujet que j’avais oublié de traiter dans une version précédente de l’article).

Ça fait déjà huit ans depuis que j’ai ouvert le site monrer.fr et il était temps que je reprenne un peu ce vieux projet, sur lequel j’avais communiqué pour la dernière fois en novembre 2015, pour y corriger de vieux problèmes latents.

Il m’a fallu beaucoup de temps pour retrouver ma motivation pour faire évoluer le site. D’abord parce que pendant longtemps, je n’avais plus trop envie de coder sur mon temps libre : c’était déjà mon activité principale pendant mes horaires de travail. D’autres limitations imposées de l’extérieur m’ont également frustré (et me frustrent encore), comme le quota d’appels de l’API Temps Réel Transilien qui est de 20 par minute et dont j’ai déjà parlé auparavant.

Mais plusieurs changements plus ou moins récents m’ont redonné suffisamment d’inspiration pour revenir sur ce projet.

Premièrement, chez mon employeur, je suis passé du domaine du développement logiciel à celui de l’ingénierie système il y a quelques années. Deuxièmement, j’ai découvert l’excellent ouvrage The Art of PostgreSQL (2e édition) de Dimitri Fontaine : les exemples m’ont émerveillé, et ce dès la page 16. Troisièmement, pour la première fois depuis la fin de mes études, j’ai pris trois vraies semaines de congé consécutifs.

Enfin, et pas des moindres : pendant ma première semaine de ces congés, un utilisateur de monrer.fr, qui me contacte déjà régulièrement lorsque le site rencontre un problème, m’a signalé un nouveau souci. J’avais en effet un bug dans une requête SQL qui doit récupérer les horaires théoriques de passage des prochains trains à une gare donnée et à un instant donné ; ce bug ne se manifestait que dans des cas très précis, notamment lors de services dégradés mis en place durant des phases de travaux estivaux. Alors quitte à remettre la main à la pâte pour corriger ce bug en particulier, autant en profiter pour résoudre d’autres problèmes plus structurels dus à des choix techniques plus ou moins discutables que j’ai faits il y a huit ans.

Accélération de la recherche des horaires théoriques

Par exemple, la recherche des horaires théoriques prenait une à deux secondes en MySQL, ce qui mettait parfois la machine à genoux durant des périodes de forte charge. Mes nombreuses soirées passées à décortiquer les sorties de commandes EXPLAIN et à indexer mes tables ont été autant d’efforts qui ne se sont pas traduits par des gains de temps spectaculaires. Par ailleurs, je souhaitais aussi avoir non seulement la liste des trains passant à une gare donnée, mais aussi, pour chaque train, la liste complète des prochains arrêts et le nom de son terminus, ce que je faisais en exécutant six fois une autre requête.

Mais en PostgreSQL, entre autres grâce à la fonction array_agg() qui me permet d’agréger un ensemble de données dans une colonne de type tableau, et aux window functions, qui me permettent d’examiner les tuples voisins dans le résultat de la même requête, je suis parvenu à une seule requête qui, en seulement 40 ms, me donne toutes les informations d’un seul coup. L’introduction d’un seul index a été décisif pour accélérer cette requête.

Par conséquent, les temps de chargement devraient être considérablement réduits pour les gares des lignes A et B, pour lesquels je ne dispose pas encore des horaires temps réel. Mais ça aussi, ça va changer.

Obtention d’accès pour l’API Temps Réel de la RATP

En effet, un autre défaut latent de monrer.fr est l’absence d’horaires temps réel pour les lignes A et B du RER. À l’époque, la RATP ne les mettait pas encore à disposition. Ça a changé depuis plusieurs années déjà, donc il ne me restait plus qu’à faire les démarches pour obtenir les accès à l’API Temps Réel RATP. Ce ne sera pas encore pour tout de suite, car l’intégration de cette API avec mon code existant est une tâche qui s’annonce complexe : la FAQ explique par exemple que le rapprochement entre les résultats de cette API et les horaires GTFS doit être faite d’après les noms des gares ; une solution qui ne me plaît guère.

Unification des gares

J’avais fait jadis l’hypothèse que chaque gare répertoriée dans mon site a un unique code TR3 (à trois lettres) et un seul code UIC (sept ou huit chiffres, utilisé comme clef par l’API Temps Réel Transilien). Cette hypothèse s’est rapidement avérée fausse, notamment :

  • lorsque la même gare dessert le RER A ou B (RATP) d’une part et une ligne RER ou Transilien exploitée par la SNCF d’autre part, comme Massy – Palaiseau ou Massy – Verrières ;

  • dans le cas des grandes gares parisiennes, où la partie « grandes lignes » et la partie « banlieue » ont des codes UIC différents, comme à Paris Gare de Lyon ;

  • ou encore quand on cumule ces deux propriétés, comme Paris Nord.

J’avais d’abord contourné le problème en dédoublant les gares concernées, ce qui n’a jamais marché de façon satisfaisante. Il fallait donc absolument revenir sur cette hypothèse.

Pour cela, il fallait donner à chaque gare un identifiant neutre, pouvant être associé à un ou plusieurs codes UIC mais servant de point d’entrée principal pour identifier une gare du point de vue d’un voyageur.

Par conséquent, il n’y a plus besoin de choisir entre « Massy – Palaiseau RER B » et « Massy – Palaiseau RER C » lorsqu’on veut les prochains départs à la gare de Massy – Palaiseau : il n’y a plus qu’un seul « Massy – Palaiseau » et les résultats peuvent ensuite être filtrés par ligne comme n’importe quelle autre gare.

Démonstration des gares unifiées
Principale conséquence de cette unification : ce genre d’horreurs fera partie du passé.

Bascule du format des horaires GTFS

J’en profite aussi pour basculer sur le nouveau format GTFS que la SNCF propose depuis peu pour son export des horaires théoriques. La loi d’orientation des mobilités (LOM) les ont en effet amenés à bifurquer cet export GTFS en un « ancien format » et un « nouveau format ».

À première vue, le nouveau format semble plus facile à exploiter pour moi : les numéros des trains (six chiffres pour un train d’une ligne SNCF et quatre lettres + deux chiffres pour un train d’une ligne RATP) sont désormais renseignés dans le champ trip_short_name de la table trips. Je n’ai donc plus besoin de faire de honteux bidouillages avec les identifiants trip_id comme je le faisais jadis. De toute manière, ces trip_id sont devenus des identifiants opaques.

Le seul inconvénient est que le rapprochement avec les gares est un peu plus compliqué car les identifiants des gares (stop_id) ne sont plus dérivés des codes UIC, mais il est possible d’obtenir une table de transcodage entre les identifiants qu’on trouve dans le GTFS et le code UIC.

Quant aux trigrammes des gares, il existe maintenant une source en Open Data : le Lexique des abréviations SNCF. La liste n’est pas complète, mais j’ai néanmoins pu corriger quelques gares prises au hasard pour lesquels le code à trois lettres était incorrect ou m’était inconnu. Mais le rapprochement entre ma propre base de données et le lexique officiel afin de vérifier et éventuellement corriger toutes les gares qui y sont répertoriées sera un projet pour plus tard.

Affichage correct des numéros de train du RER D

En règle générale, les trains ayant un numéro impair s’éloignent de Paris et ceux ayant un numéro pair s’en rapprochent. Ce qui donne aussi le nom au sens de circulation sur une ligne de chemin de fer, qu’on désigne par « pair » et « impair ».

Mais les numéros des trains du RER D sont particuliers car les trains traversant Paris changent du numéro entre Paris Gare de Lyon et Châtelet – Les Halles, un fait qui m’a longtemps posé problème pour le rapprochement entre horaires temps réel et horaires théoriques (et qui, visiblement, a aussi provoqué des dysfonctionnements du côté des vrais écrans Infogare il y a longtemps). Par exemple, un train à destination de Villiers le Bel, portant le numéro 126658 à son départ de Corbeil-Essonnes, devient le 126659 lorsqu’il quitte la Gare de Lyon pour Châtelet – Les Halles. De même, le train 153692 partant de Goussainville à destination de Melun devient le 153693 lorsqu’il quitte Châtelet – Les Halles pour la Gare de Lyon.

Pendant longtemps, pour ces trains-là, l’API Temps Réel Transilien me donnait le numéro du train valable à la gare pour laquelle je demandais les horaires : tantôt 126658, tantôt 126659 pour le premier exemple. Mais depuis le mois de novembre 2019 environ, l’API renvoie le numéro composite 126658-126659, ce à quoi j’ai réagi par un bricolage où je gardais systématiquement le numéro pair. Depuis, les numéros des trains affichés pour les gares du RER D n’étaient pas toujours les bons.

Afin de faire les choses proprement, j’ai dû ajouter aux gares de la ligne D deux informations : un numéro d’indice unique au sein de la ligne, où ma convention a été de choisir des numéros croissants dans le sens nord-sud et un booléen qui indique un sens impair dans le sens des numéros croissants s’il est vrai et un sens pair sinon ; ce booléen est donc faux pour Châtelet – Les Halles et toutes les gares au nord de celle-ci et vrai pour Paris Gare de Lyon et toutes les gares au sud de celle-ci. Avec ceci, il m’est possible de déterminer la « parité » d’un train au passage d’une gare donnée, puis lequel des deux numéros, pair ou impair, je dois afficher. Avec ceci, les numéros des trains du RER D sont de nouveau affichés correctement.

Par ailleurs, le rapprochement entre horaires temps réel et théoriques est simplifié car les exports GTFS utilisent aussi ces numéros de train composites.

J’estime aussi que ce cas, particulier au RER D à l’heure où j’écris ces lignes, pourrait aussi concerner le RER E lorsqu’il sera prolongé vers l’ouest. Dans ce cas, je suppose que les trains changeront de parité en quittant Haussmann Saint-Lazare.

Migration prévue le week-end du 15 août

Je souhaite vous faire profiter de mes modifications le plus vite possible, mais je sais que la bascule ne se fera pas en un claquement de doigts. C’est pourquoi je serai contraint de mettre le site hors ligne quelques instants durant le week-end du 15 août prochain. Je m’excuse donc par avance des désagréments que pourraient causer cette courte indisponibilité.