Journal Être averti de ses emails intelligemment

Posté par (page perso) .
Tags :
14
3
avr.
2013

Ah ! 'Nal !

Je préfère t'avertir dès maintenant : tu es un journal, je te raconte ma vie.

Combien de fois n'ai-je pas lu ce genre de théorie : GMail c'est pas bien, il faut s'auto-héberger!. Au fil de mes expériences dans le libre, j'ai moi aussi peu à peu succombé à cette théorie, et ai même installé mon propre MTA, OpenSMTPd, pour essayer de m'émanciper de cette grande prison si dorée (et puis aussi, je dois l'avouer, parce que je suis curieux).

Malheureusement, c'est pas aussi simple que ça. Comme tu le sais, s'auto-héberger ne se fait pas d'un claquement de doigt; tu pourras demander au justicier Zenitram, toujours prêt à aller contre le courant avec ses satanés arguments, ou même à n'importe qui ayant réellement tenté l'aventure. Impossible de tanner mes contacts et de leur dire Je ne suis plus disponible à cette adresse, veuillez me contacter à celle-ci avant d'avoir quelque chose d'un peu stable. Bon, très bien, je m'en vais jouer avec les paramètres de GMail et lui dire de forwarder chaque mail sur mon domaine, pour pouvoir jouir de mes emails chez moi… Horreur ! J'ai l'outrecuidance d'avoir un nom de domaine et un nom d'utilisateur trop longs, et ça ne respecte pas la RFC!

Bon, en attendant que GMail règle le problème de son côté (hahaha), il va me falloir une autre solution. J'avais configuré, depuis quelque temps déjà, OfflineIMAP pour synchroniser mes emails. Ça marche, tout va bien… sauf qu'il faut lancer à la main. C'est quand même un peu stupide de lancer le bidule à la main à chaque fois et d'attendre qu'il ait fini avant de m'apercevoir que non, je n'ai pas d'amis. Pour la petite note, OfflineIMAP a été fait pour assurer l'intégrité des messages avant tout, au détriment des performances. Un choix qu'on ne peut certainement pas reprocher a ses créateurs, mais ça veut tout de même dire 30 secondes à attendre à chaque fois… cron, tu me dis ? Oh, mais ça voudrait dire demander toutes les X minutes si quelque chose de nouveau s'est passé. Les serveurs de GMail ont beau être taillés pour, s'il y a une chose que je déteste, c'est le polling. Il va me falloir autre chose.

Je fouille dans ma mémoire et je me souviens, après mes quelques pérégrinations avec IMAP, que celui-ci avait une commande IDLE qui disait au serveur "dès que tu as quelque chose de neuf, fais moi signe !". Parfait ! Exactement ce qu'il me faut ! Bon, c'est pas dans cette RFC, c'est dans l'autre, mais GMail la supporte. Après quelques recherches chez Google (décidément, celui-ci est partout), je suis surpris qu'il n'y ait pas de solution simplissime pour être averti d'un email. Oh, il y a bien quelques petits machins qui avertissent l'utilisateur de nouveaux messages, mais j'ai peur qu'ils ne fassent rien d'autre que du polling à intervalle prédéfini. Et puis, bon, un simple IDLE, ça doit pas être compliqué à mettre en place quand même ?

Et bien voilà, un script ruby fait à la main, qui me donne tout ce dont j'ai envie :

require 'net/imap'
require 'trollop'

opts = Trollop::options do
  opt :login, "Login", :type => :string
  opt :pass, "Pass", :type => :string
  opt :mailbox, "The mailbox to watch", :default => "[Gmail]/All Mail"
end

raise "No login !" unless opts[:login]
raise "No password !" unless opts[:pass]

def debug str
  puts "[#{Time.now.to_s}] #{str}"
end

# Start of execution
imap = Net::IMAP.new 'imap.gmail.com', ssl: true
imap.login opts[:login], opts[:pass]
imap.select opts[:mailbox]

Thread.new do
  debug "Starting timer"
  loop do
    sleep 29 * 60
    imap.idle_done
  end
end

# A nice message for user
debug "Starting idle loop over here"

loop do

  begin
    imap.idle do |resp|
      if resp.kind_of?(Net::IMAP::ContinuationRequest) and resp.data.text == 'idling'
        # Server has received the IDLE order and will start emmitting stuff when a new mail arrives
        debug "Starting idle loop over there"
      end

      if resp.kind_of?(Net::IMAP::UntaggedResponse) and resp.name == 'EXISTS'
        # New mail ! We don't read what the server sent us, we just use the fact that there is something new
        debug "Running sync" 
        system('offlineimap -u Quiet')
        debug "Ran sync"
      end

    end
  rescue Errno::ECONNRESET
    debug "Connection reset by peer"
    retry
  end

end

Quelques explications, en vrac :

• tu vas avoir besoin de la gem trollop. Si tu ne l'as pas déjà, tu verras à quel point il est plaisant de travailler avec.
• je considère que tu as correctement configuré offlineimap pour qu'il se lance tel quel. Il faudra donc mettre le mot de passe en clair dans un fichier…
• Il sert à quoi ce Thread en plus ? La RFC sur IDLE dit qu'un serveur peut considérer un client ayant passé un IDLE comme étant inactif au bout de 30 minutes sans activité, et le déconnecter. Cette même RFC conseille donc que le client déconnecte l'IDLE (en l'annoncant au serveur) et en relance un. Un keepalive propre, en somme. Du coup, lorsque le Thread lancera imap.idle_done, le bloc imap.idle va se finir, et la boucle va relancer un imap.idle. On est reparti pour un tour. En tout cas, pas de souci côté ruby, l'objet Net::IMAP est thread-safe.

Tu l'auras deviné, ce n'est pas du très haut-niveau, mais ça marche pour ce que je lui demande de faire. Le but était d'avoir le moins de dépendances possibles (bon, il y a quand même trollop, mais cette gem vaut de toute façon le coup d'être installée). À part ça, c'est du ruby standard, testé sur MRI 1.9.3

Comment ça se lance ? Après avoir installé un interpréteur ruby et la gem trollop, tout se fait dans le terminal. Personnellement, je l'utilise comme ça :

$ ruby idlewatch.rb --login "login" --pass "mot de passe" || notify-send -u critical idlewatch "just crashed :("

ça me permet d'être averti quand un problème se passe, grâce à notify-send. Dès qu'il y a un problème, je peux aller voir le terminal et lire ce qu'il s'est passé. Note qu'il y a un autre argument, mailbox, que je n'utilise pas, et qui te permet de choisir la boîte à surveiller. Chez GMail, celle qui stocke tout les emails est [Gmail]/All Mail; un choix judicieux.

Je lance le tout dans un tmux pour faire tourner en arrière-plan, mais screen ferait parfaitement l'affaire. Le minimaliste dtach a même été conçu pour ce cas d'utilisation.

Je te livre ça, 'Nal, en espérant que ça puisse te servir d'une quelconque façon. Retiens tout de même que mon souhait de base était d'avoir un script le plus simple possible. Pas de paquets, pas d'installateur, juste un script que je peux coller dans mon éditeur de texte préféré et bidouiller comme bon me semble. J'espère également que la soupe de formatage markdown n'aura pas été indigeste.

Note: si question se pose, tout ce que vous voyez dans ce présent journal est placé sous licence CC0.

  • # Quick synchros d'offlineimap

    Posté par . Évalué à 3.

    Dans la conf d'offineimap proposée je trouve ce bloc de commentaires.

    # You can have offlineimap continue running indefinitely, automatically
    # syncing your mail periodically.
    […]
    # OfflineImap can replace a number of full updates by quick
    # synchronizations.  It only synchronizes a folder if 1) a Maildir
    # folder has changed, or 2) if an IMAP folder has received new messages
    # or had messages deleted, ie it does not update if only IMAP flags have
    # changed.
    
    

    Les quick synchronizations ne seraient pas ce que tu proposes ? Et s'il s'agit de polling est-il vraiment préjudiciable ?

    Ceci est une vraie question, je ne connais pas bien le sujet.

    • [^] # Re: Quick synchros d'offlineimap

      Posté par (page perso) . Évalué à 3. Dernière modification le 03/04/13 à 10:20.

      Même sans aller dans la conf d'offlineimap, sur la page d'accueil, je vois :

      Finally, you can use IMAPs IDLE infrastructure to always keep a connection to your IMAP server open and immediately be notified (and synchronized) when a new mail arrives (aka Push mail).

      Je n'y connais rien non plus, mais ne serait-ce pas ce que tu voulais faire ?

      • [^] # Re: Quick synchros d'offlineimap

        Posté par (page perso) . Évalué à 1.

        Si, c'est exactement ce que je voulais faire. La doc mentionne tout de même quelques limitations. Je me demande s'il est possible de faire autre chose qu'une synchronisation (afficher une notification sur le bureau, en envoyer une via SMTP/XMPP, …). Mais pour les cas courants, ça devrait répondre correctement au problème.

  • # Fetchmail

    Posté par (page perso) . Évalué à 7.

    Je n'ai peut-être pas bien compris, mais Fetchmail ne répond-il pas à ton besoin ?

    • [^] # Re: Fetchmail

      Posté par . Évalué à 1. Dernière modification le 03/04/13 à 11:03.

      Très bonne remarque.

      J'auto-héberge mon SMTP depuis 12 ans (avec domaine perso), et je rapatrie les messages de la BAL fournie par mon FAI, Gmail et d'autres en POP3S avec fetchmail, qui fait très bien son boulot, peut s'activer avec le cron ou bien être en démon avec intervalle de collecte configurable, et qui supporte les protocoles over TLS/SSL.

      Et un serveur IMAP à la maison, c'est très réactif (en comparaison, il n'y a pas beaucoup de monde qui peut s'offrir une fibre 1 Gbps vers les serveurs de courrier de son FAI ou de Google).

      Quant au serveur SMTP secondaire, bien utile en cas de problème, c'est un service qui peut facilement s'échanger avec un autre auto-hébergeur.

      • [^] # Re: Fetchmail

        Posté par (page perso) . Évalué à 1.

        Et un serveur IMAP à la maison, c'est très réactif (en comparaison, il n'y a pas beaucoup de monde qui peut s'offrir une fibre 1 Gbps vers les serveurs de courrier de son FAI ou de Google).

        Comprend pas. Avec un client IMAP, c'est aussi réactif (chez moi du moins, sans fibre, ou n'importe où je suis allé en wifi) : la partie IMAP ne sert qu'à synchroniser (push), le serveur IMAP est logé à la même enseigne sauf que c'est un maillon en plus donc plus lent en fait, après c'est le client qui travaille (recherche de mail, déplacements ou que sais-je), et c'est immédiat.
        J'ai du mal à comprend l'avantage d'un serveur IMAP local quand le serveur d'origine fait aussi IMAP : au niveau vitesse, vu que ça se passe sur le client, ça ne change rien.

        Quel scénario fait que pour toi c'est plus rapide d'avoir un serveur IMAP local?

        • [^] # Re: Fetchmail

          Posté par . Évalué à 1. Dernière modification le 03/04/13 à 12:03.

          Tu dois parler d'un quelconque mode "hors ligne" (ou "hors connexion") de ton client IMAP, ce qui n'a rien de standard.

          Selon la RFC-3501, les messages sont censés rester sur le serveur, et être chargés à la demande (lors de la lecture d'un message/pièce jointe), mais en aucun cas être re-stockés localement par le client. Il y a POP pour cela.

          • [^] # Re: Fetchmail

            Posté par (page perso) . Évalué à 1.

            Tu dois parler d'un quelconque mode "hors ligne" (ou "hors connexion") de ton client IMAP, ce qui n'a rien de standard.

            Donc on remplace un mode hors ligne non standard (client IMAP) par un autre mode hors ligne non standard (serveur IMAP intermédiaire).
            Euh…

            Selon la RFC-3501, les messages sont censés rester sur le serveur, et être chargés à la demande (lors de la lecture d'un message/pièce jointe), mais en aucun cas être re-stockés localement par le client. Il y a POP pour cela.

            Non, il y IMAP pour ça, POP n'a rien à voir la dedans (lien coupé entre le client et le serveur).
            IMAP n’interdit pas de charger quand on le souhaite (genre faire un cache).
            Bref, pour le moment je vois surtout un non-problème de réactivité. Juste de mauvais outils, et des bidouilles pour "corriger" les mauvais outils.

            Bref, j'aimerai un vrai exemple dans la vraie vie, un truc sur l'usage, pas un "c'est non standard" dogmatique foireux.

            • [^] # Re: Fetchmail

              Posté par . Évalué à 1.

              Donc on remplace un mode hors ligne non standard (client IMAP) par un autre mode hors ligne non standard (serveur IMAP intermédiaire).

              Le serveur IMAP auto-herbergé n'est pas pour moi un serveur "intermédiaire" mais le serveur principal (et alimenté par le SMTP de mon domaine).

              Le rapatriement via fetchmail servant à récupérer les messages de BAL "secondaire" à mon sens (BAL fournie d'office par le FAI, etc). Ce qui était le sujet de ce journal, du reste.

              • [^] # Re: Fetchmail

                Posté par (page perso) . Évalué à 1.

                Le serveur IMAP auto-herbergé n'est pas pour moi un serveur "intermédiaire" mais le serveur principal

                Ok, ça a du sens dans ce cas (il en faut bien un!)

                A la base, je réagissais surtout sur la partie "réactivité" (puis j'ai disgressé à cause de l'argument foireux), je voudrais toujours un exemple où ton serveur IMAP local apporte plus de réactivité en condition réelle par rapport à un IMAP distant, car en attendant c'est pas très réaliste de parler de meilleure réactivité et pas vraiment un argument.

    • [^] # Re: Fetchmail

      Posté par . Évalué à 1.

      Avec fetchmail, basiquement, tu reçois un nouveau message sur ton serveur, avec des en-têtes modifiés, des flags en vrac par rapport à l'original…. tu vas certainement devoir te refaire aussi les tris suivant les dossiers.
      offllineimap te garde et copie la structure du mail a l'identique. au passage, je suis pas sur qu'il l'efface du serveur d'origine, par défaut.

      bref, c'est pas les mêmes outils, bien qu'ils récupèrent tous les deux un mail pour le poser sur le serveur d'en face.
      dans ce cas précis d'exemple d'utilisation, le résultat assez proche, mais tout dépends de ce que tu veux.

      • [^] # Re: Fetchmail

        Posté par (page perso) . Évalué à 1.

        offllineimap te garde et copie la structure du mail a l'identique.

        Pas forcement.

        Dans le cas le plus strict (RFC 3501), offlineimap ajoute un entête X-OfflineIMAP pour pouvoir suivre les évolutions de ce message entre les différentes synchronisations. Pour qu'il ne touche pas aux messages (personnellement, j'aime pas trop que quelqu'un d'autre que mon MTA ou mon MUA y touche, en tout cas de mon cote), il faut implémenter UIDPLUS, qui permettra a OfflineIMAP de suivre les messages sans avoir besoin d'y apposer sa marque. UIDPLUS est implémenté chez GMail, bien sur, mais ce n'est pas dans le standard IMAP.

        • [^] # Re: Fetchmail

          Posté par . Évalué à 1.

          Je trouve ca plus discret qu'une nouvelle couche de champs "Received from" comme avec fetchmail.

          bref, tous dépends des besoins: je préfère cantonner fetchmail à la centralisation de mails, et garder offlineimap/imapcopy/etc… pour d'autres taches.

  • # Quelque chose d'équivalent

    Posté par (page perso) . Évalué à 3.

    • [^] # Re: Quelque chose d'équivalent

      Posté par (page perso) . Évalué à 1.

      Ah, je me disais bien que quelqu'un avait du faire quelque chose dans ce genre !

      Note quand même les dépendances : Twisted et pyopenssl. En plus il faut lire un fichier externe. Rien de sorcier, mais pour le cas courant d'utilisation, rien de strictement nécessaire (si c'est pour modifier le fichier une fois par an, juste pour avoir quelques paramètres faciles a changer, autant les mettre directement dans le script).

  • # Pour te consoler ...

    Posté par (page perso) . Évalué à 1.

    Horreur ! J'ai l'outrecuidance d'avoir un nom de domaine et un nom d'utilisateur trop longs, et ça ne respecte pas la RFC!

    Sur les derniers snapshots, la taille acceptée par OpenSMTPD pour les user et domain parts a été augmentée, du coup ton problème est temporaire et il deviendra du passé dans un futur pas trop lointain ;-)

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.