Auteur/autrice : Éric

  • Un petit programme par l’IA

    J’avance sur mes outils de sauve­garde mais aussi sur mes explo­ra­tions IA.

    J’ai eu besoin d’un second programme qui va lire tous les emails d’une boite au format mail­dir, regar­der l’an­née du mail, et le dépla­cer dans une boite mail­dir spéci­fique à cette année là.

    J’au­rais pu le faire en Javas­cript ou en Ruby mais vu ce que m’a fait l’IA en quelques minutes précé­dem­ment, je me suis dit que j’al­lais conti­nuer et refaire un script Go (je n’ai jamais codé une seule ligne de Go).

    Voici le résul­tat : github.com/edas/split-mail­dir-by-year.

    Les 300 lignes de Go ont analysé l’in­té­gra­lité de mon archive Gmail (537 000 emails quand même).

    • Je ne crois pas avoir touché le code source à la main
    • Le code a toujours compilé du premier coup
    • Le code a toujours fait ce que je souhai­tais, sans erreur

    Le code est correc­te­ment struc­turé, des fonc­tions ont été créées au fur et à mesure des besoins quand le code a évolué. Quand une fonc­tion est un peu longue, il sépare en blocs et ajoute une ligne de commen­taire pour dire ce que fait le bloc, ce qui me permet de ne pas avoir à déco­der un code dans un langage que je ne connais pas.

    10 demandes courtes pour avoir le programme et le faire évoluer vers mes besoins, ques­tions de relec­ture incluses. 17 ajouts par la suite pour trai­ter des cas spéci­fiques rencon­trés.

    Je n’au­rais pas fait plus vite moi-même, ni en Go ni dans un langage que je connais très bien. Peut-être que ça aurait été un peu plus diffi­cile pour un non-déve­lop­peur, mais je vois mal ce que j’au­rais eu à y gagner à le coder à la main.

    À chaque modi­fi­ca­tion j’ai le diff à vali­der mais aussi une bonne expli­ca­tion de l’IA sur ce qui a été modi­fié, comment et pourquoi. Ma relec­ture s’est souvent faite en diago­nale sur la base des commen­taires de code. L’IA a su répondre à mes ques­tions quand j’ai rencon­tré des éléments moins évide

    Plutôt que lister les étapes, je copie direc­te­ment mes prompts.


    I have hundreds thousands of files in the "maildir/new" directory. Each file contains a raw email with headers.

    I want a program which reads all emails one by one, look for the "Date" header, return an error in the header doesn't exists or is unreadable, and otherwise move the email file in the directory "by-year/{year}/new" where {year} is the year in the Date header.

    Je relis parce que ça va toucher des données réelles et que j’ai la flemme de faire des données de tests.

    Je vois qu’il retourne toujours une date même quand il y a une erreur. Inha­bi­tué de Go, j’ai peur de certaines erreurs de débu­tants en PHP ou en JS, où on utilise une date du jour plutôt que gérer l’er­reur. Je pose ma ques­tion et je suis rassuré par sa réponse (que je trouve logique après coup vu le fonc­tion­ne­ment des erreurs en Go)

    can the parseEmailDate return nil when it doesn't find a Date header ?

    Je vois aussi un mkdir sans test d’exis­tence préa­lable. Dans d’autres langages ça jette une erreur si le réper­toire existe. Je pose la ques­tion et là aussi je suis rassuré par sa réponse.

    what if the directory already exists line 63 ?

    Je demande une adap­ta­tion, non stric­te­ment néces­saire, pour que chaque réper­toire soit bien une boite mail­dir avec les 3 réper­toires obli­ga­toires. Ce n’est pas néces­saire à ma sauve­garde mais je préfère, au cas où ça m’évite des erreurs un jour.

    Oui, j’ai parfois basculé en français. Je ne sais ni pourquoi j’ai du français ici, ni pourquoi j’ai mis de l’an­glais avant. L’IA est confi­gu­rée pour toujours me répondre en français. Le code est toujours commenté en anglais. Je pense que propres entrées dépendent ce sur quoi mon atten­tion était à ce moment là (code, page web, etc.)

    Si le répertoire "by-year/{year}" n'existe pas, il faut aussi créer "by-year/{year}/cur" et "by-year/{year}/tmp", même si nous ne nous en servons pas

    Seconde adap­ta­tion : L’IA m’a dit plus haut qu’elle avait un code de gestion de conflit. Je vois le commen­taire dans le code qui dit qu’en cas de conflit le code ajoute un suffixe avec le times­tamp du moment pour éviter d’écra­ser un fichier exis­tant. Norma­le­ment ça ne devrait jamais arri­ver mais un suffixe risque­rait de casser le format de nommage des fichiers mail­dir donc je préfère qu’on s’ar­rête avec une erreur et que j’avise.

    if there is a conflict, to not append a timestamp to make it unique. Return an error.

    Troi­sième adap­ta­tion. Je traite un demi-million de fichiers. Je préfère que ça traite les fichiers au fil de l’eau plutôt qu’a­voir la liste d’un demi-million de fichiers en mémoire.

    Au départ c’est d’abord une ques­tion. Je ne sais pas si Go retourne un tableau ou un itéra­teur (oui, j’ai été flem­mard jusqu’à ne même pas faire atten­tion au typage). Je m’at­ten­dais à deman­der la correc­tion dans le premier cas. Au final il modi­fie de lui-même le code à partir de la seconde ques­tion pour faire des itéra­tions par lots 100 fichiers, sans que je ne le demande expli­ci­te­ment.

    En réalité c’est du script maison, qui sera lancé juste une poignée de fois. L’op­ti­mi­sa­tion est tota­le­ment inutile mais je n’ai pas encore appris à tota­le­ment lâcher prise vis-a-vis de ce que j’au­rais codé moi-même.

    What does return ReadDir in line 88 ?
    Si le répertoire contient des millions de fichiers, est-ce que la variable files ligne 88 va tout avoir en mémoire ?

    Je vais jouer avec de vraies données. Je veux voir les erreurs et m’ar­rê­ter pour corri­ger, pas que ça conti­nue et que j’ai à remon­ter voir s’il y a eu des erreurs.

    The programm should stop at the first error, not continue with the next file

    Et, parce que je n’y avais pas pensé avant :

    Le programme doit aussi prendre un chemin en argument. C'est là que se trouveront les différents répertoires prévus.

    La première phase est faite. Je passe au test en condi­tions réelles, sur le demi-million d’email de mon archive. Chaque fois que j’ai une erreur, je lui indique et j’avance.

    C’est là que je vois que chaque client email fait bien ce qu’il veut avec les entêtes. J’ai croisé un nombre inat­tendu de formats diffé­rents et d’er­reurs dans les entêtes. Chaque fois le programme m’af­fiche l’er­reur, je copie-colle la date problé­ma­tique, l’IA corrige, et je relance jusqu’à l’er­reur suivante.

    We should also parse the format for "Mon, 21 Aug 2006 16:47:08 +0200 (CEST)"
    We should also parse the date "Mon, 1 Dec 2008 10:57:10 UT"

    Sur une erreur étrange, j’ouvre l’email et je me rends compte qu’il prend en compte la conti­nua­tion d’une entête Recei­ved comme si c’était une date, parce qu’il ne prend pas en compte les espaces avant le mot clé Date.

    TrimSpave at line 30 should only trim right space, not left space

    Parti­cu­la­rité Gmail, quand il récu­père un email d’une boite tierce (via POP3 ou IMAP), il crée des entêtes à lui, saute une ligne et après pose le vrai email. Rétros­pec­ti­ve­ment je pense que j’au­rais dû reti­rer la section ajou­tée par Gmail pour retrou­ver un email normal. Je le ferais peut-être plus tard. Là je me suis contenté de lui faire contour­ner le problème.

    When we find the header "X-Gmail-fetch-Info", we should ignore the blank line following if it exists

    Encore des ques­tions de dates…

    We should be able to parse the Date "Tuesday 29 May 2007, 16:03"
    We should also parse "Wed, 03 Mar 2010 22:36:13 +0100 CET"
    We should also parse "Thu, 22 Jul 2010 23:02:50"
    We should also parse "Mon, 30 Mar 2009 20:11:22 +0100"

    Ce coup-ci ça ne corrige pas mon problème. Rétros­pec­ti­ve­ment j’au­rais pu le comprendre parce que le message d’er­reur n’était pas exac­te­ment le même, mais je le laisse trou­ver seul. Le mot clé DATE était en majus­cules, c’était la première fois.

    pourtant le script fait une erreur sur la ligne "DATE: Mon, 30 Mar 2009 20:11:22 +0100". Pourquoi ?

    Le code qu’il me génère imbrique quatre fonc­tions de mani­pu­la­tion de texte sur une seule ligne. Je ne trouve pas ça lisible. Je pose la ques­tion.

    que fait la ligne 49 ?

    Ça semble redon­dant avec la ligne suivante, pré-exis­tante. Effec­ti­ve­ment, quand je pose la ques­tion il iden­ti­fie le doublon et le supprime.

    Il faut penser à relire (même si l’er­reur aurait juste était du code inutile). Cursor me fait vali­der chaque chan­ge­ment sous forme de diff donc c’est assez rapide et facile à faire.

    que fait la ligne 50 ?

    Encore des formats de date…

    Encore un format : "mon, 10 jul 2006 01:02:08 gmt"
    encore un : "wed, 23 jun 2004 01:19:32 cest"
    encore un "mon, 22 mar 2010 14:20:15 +0100 +0100". C'est probablement une erreur d'écriture mais il faut la prendre en compte
    "wen, 16 jul 2008 22:09:05 +0200"

    Les deux derniers cas sont forcé­ment des erreurs de la part de clients emails. Pour la première erreur il choi­sit d’igno­rer toutes les répé­ti­tions du déca­lage horaire.

    La seconde erreur est inté­res­sante parce que « wen » est proba­ble­ment là pour « wed » (wednes­day). Il iden­ti­fie l’er­reur et ajoute un code qui remplace toute une liste d’er­reurs de frappes habi­tuelles pour les code courts de jour de la semaine. Parfait.

    "wed, 19 apr 2006 12:15 -0800"

    J’ai mon premier cas d’email sans entête « Date ». Je ne sais pas si c’est auto­risé ou non mais peu importe. Je lui dis de fouiller les entêtes « Recei­ved » à la place. Je sais que ces entêtes peuvent être sur plusieurs lignes.

    L’IA va plus loin que moi, sait que la date est en seconde posi­tion dans ces entêtes, et regarde unique­ment après le premier point virgule. Elle sais aussi comment s’ap­pellent ses entêtes sur plusieurs lignes (lignes de conti­nua­tion). Mieux que ce que j’au­rais fait.

    Je note que je tape vite, avec des erreurs de frappe, un guille­met en trop, etc. Peu importe, c’est destiné à l’IA. Me relire est super­flu : je peux reve­nir en arrière si c’est mal compris.

    If you don't find any Date header, try again to look if you can find a date somewhere in a "Received" header (theere may be multiple "Received" headers") or in the lines begining with a space and following a "Received" header
  • Disque de sauve­garde

    J’ai un SSD de 8 To dédié à ça. L’idée c’est de me donner un peu de mou sachant que j’en utilise au moins 3 To.

    Je l’ai bran­ché sur un Mac mini, qui est la seule machine fixe chez moi et je n’ai pas envie d’al­ler ache­ter un NAS en plus.

    Le tout sera aussi sauve­gardé en ligne au cas où le disque fait défaillance (mauvaise mani­pu­la­tion, panne, vol, incen­die, dégât des eaux) mais j’ai besoin que ce soit d’abord en local et pas unique­ment dépen­dant d’un service en ligne.

    Outre la défiance vis-a-vis des services en ligne, ça me permet­tra de tout remon­ter sans attendre des jours à tout télé­char­ger.

    Montage du disque

    C’est une machine parta­gée, donc il faut des droits d’ac­cès. Macos monte par défaut tous les disques USB avec un para­mètre noow­ners qui fait que tout le monde a les mêmes droits sur tous les fichiers.

    Pour corri­ger ça il faut l’em­pê­cher de monter le disque auto­ma­tique­ment et le faire à la main avec l’op­tion owners. Je ne sais pas si c’est redon­dant mais j’ai eu des succès en ajou­tant la commande diskutil enableOwnership avec l’iden­ti­fiant du disque;

    C’est un disque de sauve­garde, je fais déjà écrire beau­coup dessus en perma­nence, il faut bien penser à ajou­ter le noatime pour éviter de géné­rer des écri­tures inutiles.

    Au final :

    # GUID du disque, les /dev/disk* ne sont pas stables
    GUID="xxxxx"
    # Point de montage
    DIR="/Volumes/data"
    mkdir -m 0750 $DIR 2> /dev/null
    diskutil mount -mountOptions noauto,nosuid,nodev,noatime,nobrowse,owners -mountPoint $DIR $GUID && diskutil enableOwnership $GUID

    On chiffre

    Le disque est chif­fré. Pour moi c’est indis­pen­sable en cas de vol. C’est aussi néces­saire si le disque semble avoir des défaillance, que je ne peux l’ef­fa­cer mais que quelqu’un risque de réus­sir à relire quand même. Je ne veux ni ne peux me permettre que tout se balade n’im­porte où.

    Je pour­rais enre­gis­trer la clé de chif­fre­ment dans le gestion­naire natif de Macos comme il m’in­vite à le faire mais je vais avoir besoin plus tard que le montage se fasse au démar­rage du disque, hors de toute session utili­sa­teur.

    Je me permets donc de mettre en clair le mot de passe du disque. Tant que le script en ques­tion n’est acces­sible que par root, ça me va.

    # GUID du disque, les /dev/disk* ne sont pas stables
    GUID="xxxxx"
    # Point de montage
    DIR="/Volumes/data"
    # Mot de passe du système de fichier
    PASS="xxxxxx"
    diskutil apfs unlockVolume $GUID -passphrase $PASS -nomount
    mkdir -m 0750 $DIR 2> /dev/null
    diskutil mount -mountOptions noauto,nosuid,nodev,noatime,nobrowse,owners -mountPoint $DIR $GUID && diskutil enableOwnership $GUID
    RES=$?
    sudo chgrp Famille $DIR
    sudo chmod 750 $DIR

    On garde actif

    Par défaut Macos démonte tous les disques USB quand on ferme la session utili­sa­teur. C’est aussi vrai pour les parti­tions chif­frées, y compris celles qu’on aurait montées à la main.

    Bref, il suffit que n’im­porte qui utilise le poste et se décon­necte pour que mon disque de sauve­garde soit aux abon­nés absents, impos­sible à remon­ter vu que seul root peut accé­der au mot de passe du disque.

    J’ai ajouté en fin de script une petite boucle qui « occupe » le disque et empêche qu’il soit démonté. Ça a l’air de fonc­tion­ner.

    cd $DIR
    while true; do
       sleep 10;
    done

    Il faut quand même pouvoir arrê­ter le système, donc le script doit savoir réagir à un signal d’in­ter­rup­tion.

    trap 'bye' SIGINT SIGTERM SIGKILL
    function bye() {
     cd /Volumes
     exit 1;
    }

    Lance­ment au démar­rage

    Je n’ai pas trouvé d’autres méthodes qu’un fichier plist dans /Library/LaunchDaemons.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
      <key>Label</key>
      <string>xxxx.backup-disk</string>
      <key>OnDemand</key>
      <false/>
      <key>UserName</key>
      <string>root</string>
      <key>GroupName</key>
      <string>wheel</string>
      <key>ProgramArguments</key>
      <array>
        <string>/usr/local/sbin/mount-data.sh</string>
      </array>
      <key>RunAtLoad</key>
      <true />
      <key>KeepAlive</key>
      <false/>
      <key>LaunchOnlyOnce</key>
      <true/>
    </dict>
    </plist>

    Le script de montage complet est dans /usr/local/sbin, en lecture unique­ment pour root.

    #!/bin/sh
    # GUID du disque, les /dev/disk* ne sont pas stables
    GUID="xxxxx"
    # Point de montage
    DIR="/Volumes/data"
    # Mot de passe du système de fichier
    PASS="xxxxxx"
    trap 'bye' SIGINT SIGTERM SIGKILL
    function bye() {
     cd /Volumes
     exit 1;
    }
    diskutil apfs unlockVolume $GUID -passphrase $PASS -nomount
    mkdir -m 0750 $DIR 2> /dev/null
    diskutil mount -mountOptions noauto,nosuid,nodev,noatime,nobrowse,owners -mountPoint $DIR $GUID && diskutil enableOwnership $GUID
    RES=$?
    sudo chgrp Famille $DIR
    sudo chmod 750 $DIR
    cd $DIR
    while true; do
       sleep 10;
    done

  • Le poids d’une machine

    Depuis un peu mois de 6 mois je suis sur une mission pour laquelle on m’a donné un PC sous Ubuntu. Dell Lati­tude 5440, CPU i7U 12ème géné­ra­tion, 32 Go de RAM. Sur le papier c’est une bonne machine pour mes usages essen­tiel­le­ment web et bureau­tique.

    En pratique c’est pénible à chaque instant. Le clavier est dur sous les doigts. Le track­pad n’est pas fluide. La webcam est juste mauvaise. Le son est faible et peu clair. La puce WIFI tombe en veille régu­liè­re­ment m’obli­geant à éteindre et rallu­mer la connexion. Je ne sais pas si je dois accu­ser l’OS ou le CPU mais le système lui-même est peu réac­tif. Le simple fait de passer régu­liè­re­ment de Mac à PC fait que je me plante systé­ma­tique­ment quand je tape certains carac­tères placés diffé­rem­ment sur le clavier ou pour des choses aussi courantes que les copier-coller.

    Bref, c’est une tannée. Je me suis depuis quelques jours auto­risé à utili­ser mon Macbook pro perso quand je suis à distance : Mon niveau de stress et de fatigue a immé­dia­te­ment signi­fi­ca­ti­ve­ment baissé.

    Stress, fatigue, effi­ca­cité, impact, initia­tive, tout ça est forte­ment corélé. Je ne sais pas ce que ma mission aurait été avec un bon maté­riel mais mon travail aurait indu­bi­ta­ble­ment meilleur.

    Ne faites pas d’éco­no­mies sur le maté­riel


    On parle chiffres plutôt que convic­tion ?

    Je pars d’un système cheap : 50 € d’abon­ne­ments SaaS indi­vi­duels par mois, 150 € de chaise de bureau, 100 € de bureau, 1 000 € d’or­di­na­teur portable, 250 € d’écran. Renou­vel­le­ment tous les 5 ans. On s’en sort à 75 € HT par mois.

    La version luxe corres­pon­dante, en prenant tout ce qu’il y a de plus cher ajoute 325 € HT par mois : 200 € d’abon­ne­ments SaaS indi­vi­duel par mois, 1 500 € de chaise ergo­no­mique, 750 € pour un bureau assis-debout, 3 000 € d’or­di­na­teur portable, 1 500 € d’écran. Ajou­tons 300 € pour un casque ou des oreillettes audio anti-bruit. Imagi­nons renou­ve­ler ça plutôt plus rapi­de­ment, tous les 3 ans.

    325 € HT, pour quelqu’un qui va en coûter 20x plus, ce n’est pas rien mais ça n’est que 5% du coût total. On renta­bi­lise très faci­le­ment ces 5% si on gagne en stress, si le meilleur ordi­na­teur permet d’évi­ter un peu de temps, si l’écran permet de gagner en effi­ca­cité avec tout visible plutôt qu’à jongler entre les fenêtres, si la bonne webcam et le casque audio permettent de mieux se comprendre et d’éco­no­mi­ser du temps ou des incom­pré­hen­sions en visio, si le bureau assis-debout et la chaise ergo­no­mique permettent une fois de temps en temps d’évi­ter un mal de dos ou de la fatigue, si les outils en SaaS permettent de faire plus faci­le­ment les mêmes tâches, etc.

    J’ai forcé le trait à la fois pour la version cheap et pour la version luxe. La réalité c’est qu’on gagne bien plus que 5% d’ef­fi­ca­cité, pour une diffé­rence de coût bien plus faible que 5% du coût.

    Essayer de gagner quelques euros ou dizaines d’eu­ros par mois sur le maté­riel en impac­tant le travail de celui qui en coûte plusieurs milliers, ça n’a juste aucun sens.


    Un bon indi­ca­teur : Si des employés utilisent leur maté­riel person­nel plutôt que profes­sion­nel quand ils en ont l’oc­ca­sion, et encore plus s’ils le ramènent au bureau, c’est qu’il y a poten­tiel­le­ment un problème à régler. Si ces employés achètent du maté­riel person­nel dans l’objec­tif unique de l’uti­li­ser au travail, votre problème est sérieux.

  • Sauve­gar­der Gmail

    J’ai un histo­rique de 25 ans d’emails et j’y tiens. Il m’ar­rive encore régu­liè­re­ment d’al­ler fouiller pour retrou­ver trace de contacts ou d’échanges d’il y a 10 ans. Je l’ai fait encore ce matin.

    Je tiens donc à assu­rer la péren­nité de tout ça. J’ai confiance en Google1 mais j’ai déjà lu les récits de personnes qui se sont vus clôtu­rer leur compte sans préavis ni possi­bi­lité de récu­pé­ra­tion. Je ne suis pas non plus à l’abri de faire moi-même des bêtises dans mon compte.

    J’ai déjà utilisé Getmail par le passé pour télé­char­ger un à un tous mes emails en prove­nance de Google. Il n’a cepen­dant pas été mis à jour pour Python3. Getmail6 semble avoir pris le relai et il a la bonne idée d’être dispo­nible sous macos via home­brew.

    POP3

    Mon compte dépasse le demi-million d’emails archi­vés. La synchro­ni­sa­tion IMAP semble prendre des années à balayer tous les emails pour savoir lesquels sont nouveaux depuis la dernière fois. C’est jouable si vous avez juste quelques dizaines de milliers d’emails. Au-delà il faut utili­ser le bon vieux POP3.

    1. Acti­ver POP3 dans les options de Gmail
    2. S’as­su­rer de le confi­gu­rer pour récu­pé­rer tous les emails et pas unique­ment ceux qui arrivent à partir d’aujourd’­hui.

    Si vous aviez déjà utili­ser POP3 par le passé, il faut lui deman­der de réac­ti­ver le télé­char­ge­ment de tous les emails, y compris ceux qui ont déjà été télé­char­gés.

    Si vous avez déjà un client email qui se connecte en POP3, passez-le en IMAP. Ils se feraient concur­rence. Vous auriez une partie des emails d’un côté et une partie de l’autre.

    Confi­gu­ra­tion

    Ma confi­gu­ra­tion getmailrc ressemble à ça :

    L’uti­li­sa­teur est l’adresse email complète. N’uti­li­sez pas votre mot de passe Google et créez un mot de passe d’ap­pli­ca­tion dédié, ça vous permet­tra de le révoquer un jour. Pensez à proté­ger ce fichier de confi­gu­ra­tion parce que celui qui y aura accès aura aussi accès à votre compte Google.

    Le para­mètre recei­ved assure que Getmail récu­père le message tel quel, sans ajou­ter d’en­tête.

    Erreurs de synchro­ni­sa­tion

    Google envoie tous les emails par groupes de 200 à 1000 non télé­char­gés à chaque appel POP3. Si le para­mètre read_all est à false, Getmail ne télé­charge pas les emails qu’il a déjà vu.

    Pour une raison ou une autre, ça peut partir en boucle si Getmail pense avoir déjà vu un email que Google pense pour­tant ne pas être déjà télé­chargé : L’email va être repro­posé à chaque fois. Si ça fait ça sur plusieurs centaines d’emails, vous prenez le risque que vous restiez bloqué sur le même groupe d’emails indé­fi­ni­ment à chaque appel.

    Véri­fiez bien que le read_all est à true. Si le problème persiste, une solu­tion est de passer par le type BrokenUIDLPOP3SSLRe­trie­ver plutôt que SimplePOP3SSLRe­trie­ver. Getmail télé­charge alors systé­ma­tique­ment les messages sans se préoc­cu­per de ce qu’il a déjà lu ou non.

    Quota

    Gmail a des quota sur ses APIs. Pour initia­li­ser mon demi-million d’emails j’ai lancé Getmail en boucle et je suis tombé sur des erreurs 403. Si vous devez comme moi initia­li­ser plusieurs dizaines de Go, ça demande un peu de surveillance.

    Le quota semble toute­fois très très large. En rythme de croi­sière je compte proba­ble­ment synchro­ni­ser 1 à 2 fois par jours mais on peut sans problèmes faire plusieurs synchro­ni­sa­tion par heures si ça vous semble indis­pen­sable.

    Mail­dir

    Il y a deux formats, mbox et mail­dir. Mbox c’est un unique gros fichier, un peu comme une archive tar. Mail­dir c’est un réper­toire avec un fichier par email, ce qui me semble plus adapté pour des sauve­gardes avec de nouveaux emails tous les jours.

    Initia­li­ser un mail­dir c’est juste un réper­toire qui contient un sous-réper­toire tmp, un new, et un cur. Tous les emails envoyés par Getmail iront dans new.

    Il reste que pour mon demi-million d’emails ça commence à faire beau­coup de fichiers dans un seul réper­toire. J’ai fait un petit script qui parcourt l’in­té­gra­lité des emails, lit la date2, et les répar­tit dans des mail­dir spéci­fiques par année. Ça faci­li­tera aussi la sauve­garde et me permet­tra éven­tuel­le­ment de faire un zip pour les vieilles années.

    Emails envoyés

    Le problème du POP3 c’est que ça ne télé­charge que les emails reçus, pas les emails envoyés. J’ai donc dû ajou­ter une seconde synchro­ni­sa­tion IMAP sur le dossier des emails envoyés. Là j’ai dans les 30 000 emails. C’est long à parcou­rir mais encore jouable tant que je ne lance pas la récu­pé­ra­tion toutes les deux minutes.

    J’ai donc deux fichiers getmailrc, un pop.getmailrc (emails reçus) et un imap.getmailrc (emails envoyés). Je dois donc manuel­le­ment spéci­fier les para­mètres –rcfile= quand j’ap­pelle Getmail. On peut spéci­fier plusieurs fois le para­mètre, les synchro­ni­sa­tions sont alors lancées l’une après l’autre.

    J’en­vois ça vers le même dossier mail­dir, puis fais la même répar­ti­tion par année.

    Enre­gis­trer en tâche plani­fiée

    Sous Linux il suffit proba­ble­ment de faire une entrée dans le cron.

    Sous Macos, si je veux que la synchro­ni­sa­tion s’exé­cute même quand je ne suis pas connecté, il faut enre­gis­trer le script dans /Library/Laun­chDae­mons.

    Comme le réper­toire desti­na­tion de mes sauve­garde est hors de mon disque prin­ci­pal, Macos impose aussi que le programme lancé ait la permis­sion spéciale « full disk access ». Getmail n’est qu’un script Python et je n’ai pas voulu donner des droits larges à tout Python. J’ai dû faire un programme natif qui lance mon script Getmail et donner les droits sur le disque à ce script là.

    Je ferai proba­ble­ment un billet dédié à tout ça.

    Entre temps j’ai presque 40 Go d’his­to­rique email à synchro­ni­ser depuis Google par batch de 200 à 1000 mails. Ça va me prendre un peu de temps.


    1. Oui, j’uti­lise Google. J’ai soupesé plein de fois les alter­na­tives mais toutes auraient demandé un compro­mis signi­fi­ca­tif et je ne suis pas encore passé à l’ac­tion. Ça vien­dra. J’ai besoin de trou­ver le bon équi­libre et de ne pas perdre la recherche dans le passé. ↩︎
    2. Je ferais un billet spéci­fique, ça s’est révélé bien moins évident que je ne l’ima­gi­nais naïve­ment. ↩︎
  • Tu veux quoi comme rôle ?

    Je crois avoir derniè­re­ment compris un truc sur les rôles ou contextes que je cherche : Je ne veux pas être restreint.

    La ques­tion n’est pas « code ou mana­ge­ment ? » ni « quel niveau hiérar­chique » ou « combien de personnes à mana­ger ». C’est « ne me restreins pas une seule case, peu importe laquelle est-ce ».

    Je veux parler code, mana­ge­ment, orga­ni­sa­tion, busi­ness, ergo­no­mie, recru­te­ment, métier, client, déploie­ment, commu­ni­ca­tion, stra­té­gie de l’en­tre­prise, support et encore plein d’autres choses.

    Les postes de direc­tion m’ont souvent permis ça dans une certaine mesure. Rétros­pec­ti­ve­ment, j’ar­rive à corré­ler me hauts et mes bas profes­sion­nels avec ma capa­cité à inter­ve­nir partout où c’était utile, ou au contraire à des contextes où je me suis senti réduit à une unique facette du problème à résoudre.

    Je ne suis pas expert de tout. J’ai ma propre exper­tise mais je ne veux pas m’y restreindre. Je veux pouvoir travailler avec les autres sur tous les sujets qui nous semblent perti­nents, chacun avec son angle et son exper­tise. Je veux pouvoir avan­cer là où mes équipes ont besoin sans savoir qui a quel rôle ou quel péri­mètre.

    Ça colle d’ailleurs avec mon passé. J’ai refusé de me limi­ter à une seule étiquette. J’ai commencé par du pur réseau mais je peux me récla­mer d’avoir été expert web back-end, expert web front-end, d’avoir déve­loppé une exper­tise métier sur le livre numé­rique. C’est déjà beau­coup. J’ai aussi fait de la forma­tion, du mana­ge­ment, de l’or­ga­ni­sa­tion. J’ai colla­boré quand j’ai pu avec la busi­ness, les rh, les ques­tions client et produit, la vie de l’en­tre­prise, etc.

    Main­te­nant que j’ai compris ça, il va me falloir défi­nir ce que ça implique dans les postes à recher­cher, et trou­ver le bon. Vous êtes — réel­le­ment — bien­ve­nus à m’y aider, serait-ce unique­ment par vos feed­backs.

  • Micro­soft Teams

    Real thing : une boite qui utilise Teams c’est main­te­nant un vrai point en moins dans mon envie de les rejoindre et il faut qu’il y ait de vrais atouts pour contre­ba­lan­cer.

  • Fire­fox, cœur du web ouvert

    Fire­fox ce n’est pas juste un navi­ga­teur. Sans Mozilla et Fire­fox, nous n’au­rions pas le web libre d’aujourd’­hui. Sans Mozilla et Fire­fox nous n’en auront peut-être plus demain, ou plus sous cette forme. Rien que ça.

    La situa­tion pré-1998

    Pour ceux qui n’ont pas vécu la période pré-2005, Inter­net c’était essen­tiel­le­ment Nets­cape, AOL et Micro­soft.
    Oui, je remonte loin mais c’est impor­tant.

    Si les stan­dards du web exis­taient déjà, il y avait beau­coup de choses non spéci­fiées, beau­coup d’ano­ma­lies propres à chaque navi­ga­teur, et beau­coup d’ex­ten­sions proprié­taires. Faire un site compa­tible avec plusieurs navi­ga­teurs deman­dait un effort parti­cu­lier, voire un double déve­lop­pe­ment.

    Nets­cape c’était le navi­ga­teur dont est issu Mozilla puis Fire­fox. Jusqu’en 1995–98, tout le monde utili­sait Nets­cape, ou presque.

    Micro­soft a pris le pas entre 1996 et 1998 avec l’ar­ri­vée de Windows 95 Plus et de Windows 98. Ils ont fourni Inter­net Explo­rer par défaut comme navi­ga­teur, inté­gré à l’OS. À l’époque on parlait même d’avoir une page web à la place de l’image de fond d’écran, et des widgets web pour avoir des actus, la bourse, la météo, etc. Tout lançait Inter­net Explo­rer sans alter­na­tive, possible, y compris la mise à jour de Windows lui-même.

    La guerre des navi­ga­teurs

    Nets­cape a lancé la suite de navi­ga­tion Mozilla en open-source. AOL a ensuite racheté Nets­cape pour avoir son propre navi­ga­teur basé sur Mozilla.

    Malheu­reu­se­ment Micro­soft a profité de sa posi­tion domi­nante sur Windows pour non seule­ment pré-instal­ler Inter­net Explo­rer partout mais aussi en faire une pièce incon­tour­nable.

    À l’époque la mise à jour de Windows se faisait par Inter­net Explo­rer. Micro­soft inci­tait même à rempla­cer le fond d’écran par une page web (gérée par Inter­net Explo­rer) et des widgets web pour les actua­li­tés, la météo, la bourse, etc. Même Apple est passé à Inter­net Explo­rer sur les Mac quand Micro­soft est entré dans le capi­tal.

    La majo­rité des sites, petits et gros, étaient conçus pour Inter­net Explo­rer et compa­tibles unique­ment avec ce dernier. Nets­cape, seule vraie alter­na­tive, n’a pas tenu.

    La bataille du web ouvert

    Des anciens de Nets­cape sont restés autour de la partie open-source de Mozilla. Avec le temps est sorti Fire­fox : ultra-léger (par rapport à Nets­cape) et ultra-rapide (idem).

    On est déjà en 2003–2005. C’est le début de Mozilla (l’as­so­cia­tion) telle qu’on la connait aujourd’­hui.

    L’an­cien Inter­net Explo­rer est mort, le web ouvert a gagné. Fire­fox a fini par deve­nir le navi­ga­teur majo­ri­taire en Europe.

    Il était infi­ni­ment meilleur que Micro­soft Inter­net Explo­rer mais il n’y a pas que ça : On a aussi eu une bataille massive pour le web ouvert, à faire chan­ger de pratique les sites web pour qu’ils respectent les normes et qu’ils assurent la compa­ti­bi­lité. Je me rappelle le web-compat de Mozilla qui inter­pe­lait et propo­sait des correc­tifs aux sites web un à un.

    C’était du mili­tan­tisme partout. C’est d’ailleurs là que — self-promo — est né Paris-Web.

    Les deux se sont nour­ris. Sans Fire­fox et Mozilla, le web ouvert n’au­rait peut-être jamais vu le jour malgré tous nos efforts. Sans les efforts sur le web ouvert Fire­fox et Mozilla auraient proba­ble­ment été des impasses.

    Garder le web ouvert

    Fire­fox et web ouvert sont histo­rique­ment intrin­sèque­ment liés. C’est pour ça que je dis que Fire­fox ce n’est pas juste un navi­ga­teur.

    Aujourd’­hui c’est Google Chrome qui a la main et on voit appa­raitre le même schéma que par le passé : diffu­sion par pré-instal­la­tion, sites qui ne sont parfois conçus ou testés que pour un seul moteur de navi­ga­tion, abus de posi­tion domi­nante, etc.

    La situa­tion n’est pas exac­te­ment la même que celle des années 2000 mais elle n’est pas si éloi­gnées que ça.

    Il y a plein de navi­ga­teurs mais en réalité Brave, Opera, Vivaldi, Inter­net Explo­rer et les autres sont des déri­vés de Chro­mium. Chro­mium c’est la base open source de Chrome, contrô­lée par Google.

    Le web est défi­ni­ti­ve­ment plus ouvert mais du fait de la main mise sur Chro­mium, Google est quand même en posi­tion forte pour faire avan­cer ce qu’il veut et refu­ser ce qu’il ne veut pas. Pas mal de sites se basent sur des APIs et exten­sions présentes dans Chro­mium mais pas encore ailleurs, ou qui n’ont pas voca­tion à être présentes ailleurs.

    Seule alter­na­tive

    Pour ne pas lais­ser l’his­toire se répé­ter, on a besoin d’al­ter­na­tives.

    Une fois qu’on a retiré tout ce qui se base sur Chro­mium, il reste Safari et Fire­fox. Les inté­rêts d’Apple ne sont pas toujours extrê­me­ment diffé­rents de ceux de Google. On a besoin d’un acteur qui sort de ces acteurs privés mono­po­lis­tiques et domi­nants.

    Fire­fox est seul dans cette situa­tion. Sa survie est juste essen­tielle pour garder un web ouvert.

  • Petit aven­ture de phishing

    Parce que ça sert toujours, petit récit d’un phishing subi par une personne de mon entou­rage.

    Un colis est attendu. Ce n’est pas à l’adresse habi­tuelle et le comman­di­taire n’est présent que pour peu de jours. Un message est reçu pour aver­tir qu’il y a eu une diffi­culté et que le colis sera fina­le­ment livré dans un relai colis.

    Il y a un lien pour avoir les infos. La page web demande des infor­ma­tions, pas mal d’in­for­ma­tions, dont au moins nom, adresse, télé­phone.

    Je n’ai pas compris pourquoi mais la page a aussi incité au paie­ment d’un montant symbo­lique par carte bancaire. Norma­le­ment c’est là que ça doit coin­cer si ça n’a pas coincé plus haut mais entre l’at­tente du colis le même jour, le stress de la nouvelle adresse avec un temps contraint pour la récep­tion, et simple­ment un mauvais réveil le matin, l’inat­ten­tion arrive vite.

    C’est là que ça devient inté­res­sant.

    Dans la jour­née la banque appelle au télé­phone, dit qu’il semble y avoir une fraude sur la carte bancaire, qu’ils l’ont repéré, qu’il y a eu d’autres paie­ments dont un paie­ment louche de 500 € sur AirBnb.

    Ils s’ac­cordent sur un rembour­se­ment, le préposé indique qu’il va y avoir une noti­fi­ca­tion à accep­ter sur l’ap­pli­ca­tion bancaire pour confir­mer.

    La noti­fi­ca­tion arrive.

    Coup de bol, ma connais­sance repère avant de cliquer qu’il s’agit d’un paie­ment et non d’un rembour­se­ment, et se rappelle la règle « on ne valide rien et on rappelle soi-même la banque pour confir­mer l’in­ter­lo­cu­teur avant quoi que ce soit, ils sauront reprendre là où on en était si l’ap­pel est légi­time ».

    La seconde étape a échoué mais ça aurait été facile de passer sans faire atten­tion : Il y a un contexte réel de phishing quelques heures avant qui rend tout ça crédible, un coup de stress, un inter­lo­cu­teur qui sait qui on est et qui renvoie vers l’en­vi­ron­ne­ment sécu­risé de la banque.

    C’était un lundi. Appel au central de gestion de la carte qui conseille de faire oppo­si­tion par acquis de conscience.

    Le lende­main, appel à la banque, qui accom­pagne. L’ac­cès au compte est verrouillé on ne sait pas pourquoi. Ils déver­rouille­ront ça ensemble.

    Je publie parce que je trouve l’en­chai­ne­ment inté­res­sant.

    On peut tous se faire avoir par du phishing pour peu que ça tombe au bon moment et que ça coïn­cide par chance avec une situa­tion réelle et un contexte d’ur­gence. Un colis coincé, les proba­bi­li­tés ne sont pas nulles1.

    Faire un paie­ment sur une page web à partir d’un lien envoyé devrait faire tiquer mais le montant symbo­lique ne réveille pas forcé­ment les alertes mentales2 s’il y a un peu d’inat­ten­tion suite à une situa­tion agaçante.

    Ce que je trouve inté­res­sant c’est que cette première étape sert en fait à iden­ti­fier les personnes qui sont suscep­tibles de se faire avoir et qui justi­fient un appel via un humain pour la vraie arnaque.

    J’ima­gine qu’une fois sur ces listes il y aura d’autres tenta­tives, et que ma connais­sance va devoir redou­bler de vigi­lance pendant au moins 18 mois.

    Il parait que la banque et le service carte ont été top. Je suis par contre furieux du déblo­cage du compte en banque au télé­phone à partir de quelques « ques­tions person­nelles ». Ces ques­tions person­nelles on peut proba­ble­ment y répondre faci­le­ment à la place de plein de proches, voire après recherche inter­net à la place de beau­coup d’in­con­nus dont on connait le nom. C’est une illu­sion de sécu­rité et c’est drama­tique qu’on se repose dessus.

    Je suis furieux mais cepen­dant pas surpris. Je n’ai connais­sance d’au­cune banque, assu­rance ou opéra­teur télé­pho­nique grand public qui fasse mieux de ce point de vue là. Dans le meilleur des cas il y a appel télé­pho­nique ou échange d’email, ce qui n’est pas forcé­ment une protec­tion si on vient de se faire voler son smart­phone.

    Bref, faites atten­tion.


    1. Je ne comprends toujours pas pourquoi tous les services de livrai­sons sont aussi mauvais. Seul Amazon semble avoir un niveau de pres­ta­tion correct. C’est hallu­ci­nant qu’il soit normal et attendu d’avoir des problèmes lors de livrai­sons. ↩︎
    2. Et, là aussi, malheu­reu­se­ment on est telle­ment habi­tués à ce que ces services de livrai­sons soient pour­ris et profitent de tout que le paie­ment suite à une anoma­lie devient presque crédible. ↩︎
  • Orga­ni­sa­tion des sauve­gardes

    Je me suis remis à mes sauve­gardes. Le dernier épisode était en septembre et ça commence à faire presque deux ans que rien n’est fina­lisé.

    Je reste sur mon plan précé­dent. J’ai juste aban­donné l’idée d’uti­li­ser Crash­plan. Ça part de messages sur des forums où ils disent expli­ci­te­ment qu’ils ne pouvaient pas garan­tir le bon fonc­tion­ne­ment quand on dépasse quelques To de sauve­garde. Derrière j’ai exploré plus et les débits famé­liques rendent de toutes façons illu­soire une restau­ra­tion complète sur des volumes de cet ordre de gran­deur en cas de défaillance disque.

    J’irai chez BorgBase ou Hetz­ner, proba­ble­ment Hetz­ner parce que même en coupant l’inu­tile j’ai quand même au moins 3 To aujourd’­hui et que va commen­cer à faire cher chez BorgBase avec l’aug­men­ta­tion natu­relle.

    Disque externe – Disque interne

    Le vrai sujet de ce week-end, c’est comment monter une parti­tion chif­frée depuis un disque externe. C’était déjà mon problème en septembre.

    Macos consi­dère que les disques externes sont comme des clés usb, lisibles par tous les utili­sa­teurs, sans droits d’ac­cès, et démon­tées dès qu’on se décon­necte de sa session.

    Passer à un système chif­fré empêche qu’il soit montable par tout le monde tant qu’on n’ajoute pas la clé de chif­fre­ment dans le trous­seau.

    Si c’est monté manuel­le­ment, on peut ajou­ter le para­mètre owners pour faire en sorte que le système respecte les permis­sions sur les fichiers et n’ouvre pas tout à tout le monde.

    J’ai pu faire monter le disque au démar­rage en ajou­tant un plist dans /Library/Laun­chDea­mons et en le char­geant avec un laun­chctl load. Le plist exécute un script au démar­rage qui déver­rouille le disque et le monte avec les bonnes options. Ça veut dire que la clé de chif­fre­ment est en clair dans un fichier du disque interne. Le disque interne est chif­fré lui aussi, le fichier n’est lisible que par root. Ce n’est pas parfait mais suffi­sant pour mon usage.

    Ça reste visible comme un disque externe, donc tout le monde peut deman­der à l’éjec­ter. J’ai palier au problème ajou­tant un petit code dans le script de démar­rage qui entre dans le disque et attend indé­fi­ni­ment. Le disque étant occupé, personne ne peut l’éjec­ter.

    J’ai l’im­pres­sion de batailler à faire du brico­lage sur ce qui m’au­rait pris quelques minutes sous Linux mais ça fonc­tionne.

    L’étape suivante ça va être de s’as­su­rer que tous les fichiers se retrouvent sur le disque prévu pour, en synchro­ni­sant tous les comptes Google Drive et Treso­rit. Ensuite je vais instal­ler un getmail pour archi­ver en temps réel les boites email, proba­ble­ment un script pour archi­ver le Github. La dernière étape sera de bran­cher Borg pour envoyer le backup en ligne et d’at­tendre un bon mois qu’il finisse la synchro­ni­sa­tion initiale.

  • Garder ouvert un script shell

    Je cherche à ce qu’un script shell reste ouvert comme un démon au lieu de rendre la main après s’être exécuté. Comme un démon, je veux qu’il réagisse en se termi­nant de lui-même quand il reçoit une demande de SIGTERM.

    Ma première approche c’est une boucle infi­nie avec un sleep.

    trap 'quit' SIGTERM SIGKILL
    
    function quit() {
      exit 1
    }
    
    while true; do
      sleep 10
    done 

    Le trap n’in­ter­rompt pas le sleep. J’ai mis 10 secondes pour garder une réac­ti­vité raison­nable à l’ex­tinc­tion.

    Même si un réveil toutes les 10 secondes est proba­ble­ment insi­gni­fiant, quelque chose en moi est quand même gêné et aurait aimé mettre plusieurs heures ici.

    Je vois sur le web pas mal d’exemples avec un sleep 1, qui m’in­ter­roge encore plus. Quel est le coût réel de ce sleep 10 dans une boucle infi­nie ?

    Certains ont élaboré des solu­tions pour rendre le sleep inter­rup­tible en l’en­voyant en tâche de fond :

    PID=
    
    trap '[[ $PID ]] && kill "$PID"' SIGTERM SIGKILL
    
    while true; do
      sleep 100000 & pid=$!
      wait
    done

    Je vois aussi, et ça m’a l’air simple & smart, des scripts utili­ser des read­line plutôt que des sleep. Les read­line ont la bonne idée d’être inter­rup­tibles et de durée infi­nie tant qu’on n’en­voie rien sur stdin.

    trap 'quit' SIGTERM SIGKILL
    
    function quit() {
      exit 1
    }
    
    read

    Dites, les amateurs de shell, quelle est la méthode recom­man­dée pour garder un script ouvert en tâche de fond ? Est-ce qu’il y a une réelle diffé­rence entre ces méthodes ou est-ce juste une ques­tion de style ?