Prévisualiser des .eml dans Gitea

  1. Contexte
  2. Problématique
  3. Prérogatives
  4. Solutions
    1. Overengineering
    2. Gitea tout puissant
  5. Résolution du problème
    1. Configuration de mhonarc
    2. Configuration de gitea
    3. Avantages, inconvénients

Contexte

Voilà un an et demi que je stocke tout dans git, et j'en suis pleinement satisfait.

L'une des rares choses que je n'y stockais pas jusqu'à maintenant sont certains emails que j'estime être d'un intérêt particulier.

Mon objectif était de pouvoir exporter manuellement un ou plusieurs messages de mon choix (généralement, au moment où j'ai fini de les lire) dans un format plus ou moins standard, et les déposer dans ma forge logicielle via les commandes git classiques.

Jusque là, aucun soucis : sous macOS, Fichier puis Enregistrer, git commit et git push, travail accompli.

Problématique

Sauf que les emails sont encodés, ce qui signifie qu'ils ne sont pas directement lisibles par un humain. Un fichier eml est comme une archive contenant tout un tas d'informations, telles que les en-têtes, parfois un corps de message en texte-brut et un autre en HTML, sans compter les pièces jointes. Il faut donc faire appel à un outil spécialisé pour extraire ces informations, et les décoder correctement.

Parce qu'avoir mes emails stockés dans git c'est bien, mais une fois qu'ils y sont, sans leur dépaquetage, sans leur décodage, ils sont difficilement exploitables, je veux surtout que Gitea m'en offre une version human-friendly.

Prérogatives

  • Je ne veux pas que le fichier d'origine soit altéré
  • Je veux pouvoir visualiser les en-têtes du message, y compris ce qui semble inutile aux humains
  • Je veux afficher la partie texte-brut du message en priorité, la partie HTML ne m'intéresse pas (sauf si c'est la seule qui existe)
  • Je veux que gitea m'affiche le fichier eml comme si j'étais dans un client mail, ou en tout cas que je puisse directement lire le mail sans devoir déchiffrer des séquences de caractères comme =C3=A9 (qui encode le caractère é)
  • Je veux que le HTML produit soit propre ("markdownisé", en gros)
  • Je veux que la solution soit légère, au point de pouvoir le faire à la demande

Solutions

Overengineering

La solution "évidente", c'est d'utiliser la CI pour faire une conversion lors d'un push. C'est apparemment la solution la plus simple, mais en fait, non :

  • je ne veux pas créer de fichiers additionnels pour des raisons de simplicité et d'espace disque
  • j'ai plusieurs dépôts en fonction des contextes des emails et non en fonction des boîtes de réception, et je n'ai pas envie de m'amuser à créer des jobs pour chacun d'entre eux dans ma CI

Gitea tout puissant

Gitea offre une fonctionnalité toute puissante que j'utilise déjà pour permettre l'affichage direct de documents au format .docx : les external renderers. Gitea permet de déclarer une commande qui prend en entrée le fichier en cours de prévisualisation et qui doit sortir le code HTML qui doit permettre sa prévisualisation.

Dans le contexte particulier des .docx, c'est pandoc qui fait ce travail, comme parfaitement expliqué dans la documentation de gitea (un exemple qui fonctionne d'ailleurs out-of-the-box).

C'est donc vers cette solution que je vais me tourner.

Résolution du problème

Ça n'a pas été simple, vu que manifestement, comme d'habitude, personne n'a jamais fait ce que j'essaye de faire…

Toutes mes recherches se sont soldées par des solutions à base de Java, de docker, et surtout, d'export en PDF, sans jamais mentionner l'intégration à gitea. Certaines évoquaient d'utiliser des outils avec interface graphique, des solutions où tu dois cliquer 12 000 fois pour avoir un fichier HTML exploitable, donc des solutions parfaitement impossibles à industrialiser.

Aucune des solutions que j'ai trouvé n'était simple à mettre en oeuvre. Aucune des solutions que j'ai trouvé ne permet de prendre en entrée un flux standard et de sortir un flux standard, autrement dit en une ligne de commande utilisant les pipes (et munpack semble créer systématiquement des fichiers supplémentaires).

Et puis, je suis tombé sur un container docker plus vieux que les autres, plus maintenu, et surtout, beaucoup plus léger que tout ce que j'ai vu jusqu'à maintenant. Dans ce container, un outil était mentionné, et un fichier de configuration était joint. Cet outil, c'est Mhonarc.

Il m'a fallut quelques secondes pour intégrer l'outil aux renderers de gitea.

Le rendu n'est pas très propre, mais après avoir digéré une partie de la (grosse) doc de l'application, j'arrive au résultat escompté.

La transformation de l'eml en HTML est presque parfaite, mais surtout, elle est quasi-instantanée. Cerise sur le gâteau, gitea m'offre un bouton pour basculer facilement et immédiatement du fichier source d'origine à sa prévisualisation en HTML. Voilà, c'est ça l'informatique que j'aime.

Configuration de mhonarc

Les options par défaut de mhonarc sont assez satisfaisantes, mais je voulais aller un chouilla plus loin. Il suffit de créer un fichier de configuration qui va substituer les paramètres à ceux par défaut, donc il peut ne faire que quelques lignes et ne cassera pas l'ensemble de ce qui est généré.

<MIMEARGS>
m2h_text_html::filter; allownoncidurls
m2h_text_plain::filter; nonfixed fancyquote maxwidth=80
</MIMEARGS>

<MIMEAltPrefs>
text/plain
text/html
</MIMEAltPrefs>

<MIMEFilters>
text/html;   m2h_text_plain::filter; mhtxtplain.pl
text/x-html; m2h_text_plain::filter; mhtxtplain.pl
</MIMEFilters>

Honnêtement, je n'ai pas encore tout compris à ce que j'ai fait ici, mais en gros, je demande à mhonarc d'utiliser en premier lieu la partie texte-brute en priorité par rapport à la partie en HTML du message d'origine, en tout cas s'il en contient une (c'est le bloc MIMEAltPrefs).

Pour les messages qui ne contiennent qu'une partie en HTML, ce HTML est transformé pratiquement en texte-brut (le bloc MIMEFilters).

Le texte brut obtenu est alors quelque peu formaté, avec un maximum de 80 caractères par ligne et l'utilisation de la balise blockquote pour les citations (c'est le bloc MIMEARGS).

Configuration de gitea

Avec NixOS, c'est facile :

{
  services.gitea = {
    # [...]
    settings = {
      # [...]
      "markup.eml" = {
        ENABLED = true;
        FILE_EXTENSIONS = ".eml";
        RENDER_COMMAND = "/run/current-system/sw/bin/mhonarc -single -rcfile /etc/nixos/environment/etc/mhonarc";
      };
    };
  };
}

À noter que je sais que la spécification des chemins est sub-optimale mais je n'ai pas encore la maîtrise de nix nécessaire pour les éviter : ça fonctionne mais c'est une mauvaise pratique. Par exemple, je crois qu'il vaudrait mieux remplacer /run/current-system/sw/bin/mhonarc par ${pkgs.mhonarc}/bin/mhonarc ou quelque chose du genre, mais je ne suis pas sûr de mon coup : je laisse le soin au lecteur de me corriger au cas où, et je mettrai cet article à jour en conséquence.

Avantages, inconvénients

Ben déjà, ça juste marche, c'est hyper léger, ça s'intègre facilement à gitea.

En revanche, j'ai des balises <html><head></head></html> qui traînent dans la sortie, et il y a certainement moyen de s'en débarasser mais je n'ai pas encore trouvé comment. Et puis j'aimerais "markdownifier" le corps du message. J'ai fait quelques essais pour piper pandoc mais je n'obtiens qu'une erreur 500 dans gitea. Enfin, j'aimerais adapter le CSS pour utiliser une fonte monospace pour le corps du message (mais ça devrait être facile à faire).

Ce sont des améliorations futures mais pour l'heure, je suis satisfait.

Plus d'informations