Gmaildb : archiver ses mails dans une base SQLite
Comme beaucoup de gens, une grande partie de ma vie est chez Google, et il est très difficile d’en sortir tellement leurs produits sont pratiques et efficaces.
Sauf que… le temps passant, je suis de plus préoccupé par deux soucis
- Pas très surprenant: Google se comporte de plus en plus en Big Brother (les révélations Snowden ont confirmé ce qui était à craindre)
- Moins connu : Google peut supprimer tout compte gratuit du jour au lendemain, sans préavis, sans recours possible, typiquement sur la base de heuristiques parce qu’un robot aura cru que vous avez fait quelque chose d’illégal ou qui contrevient aux termes de service. ça fait pourtant longtemps que des gens tirent la sonnette d’alarme. Plus récemment, on voit de plus en plus de Youtubeurs chercher des financements alternatifs car il est très fréquent de se faire démonétiser une vidéo ou de subir l’excès de zèle des robots Google vis à vis de portions de contenu suspectées d’être potentiellement copyrightées… Et quand Google frappe, il frappe parfois fort (ce qui peut aller jusqu’à l’ensemble de la destruction dudit compte, donc pas seulement youtube mais aussi gmail, contacts, agenda, etc. Du jour au lendemain, votre vie envolée !
C’est un peu dans ce contexte que des associations comme Framasoft ont créé des initiatives telles que Dégooglisons internet (que je ne peux que soutenir !). Mais en attendant, la première chose est de sauvegarder ses données (quel que soit le fournisseur de services cloud et que ce soit pour migrer ailleurs ou juste par précaution).
Il y a longtemps, j’avais fait un backup d’un ancien compte gmail (que j’ai fermé depuis) grâce à l’outil gmvault (N.B. d’autres outils existent ou sont apparus depuis, comme par exemple GotYourBack). Ce dernier stocke chaque mail sous la forme deux fichiers : un fichier .eml (au format MIME classique) et un fichier .meta (JSON avec les métadonnées propres à GMail tels que les labels) dans un dossier db/<month>/
. J’avais archivé le tout dans une archive squashfs (beaucoup plus efficace et plus pratique à utiliser que du zip, en tout cas sous Linux). Donc j’étais serein: mes mails étaient bien archivés, mais pas très facilement consultables sous cette forme sans les réimporter dans un client mail classique (tel que Thunderbird). Plus récemment, j’ai utilisé Google Takeout pour réaliser un backup complet de mon compte Google personnel actuel, et dans ce cas Google renvoie un énorme fichier au format mbox (directement utilisable, mais qui casse l’encodage des mails non-UTF8 comme on verra plus loin). Dans les deux cas (backup gmvault, backup Takeout) j’étais un peu perfectionniste et ne me contentais pas de les réimporter dans Thunderbird car je me suis dit que ce serait pratique si je pouvais
- stocker le texte des mails dans une base de données SQLite pour pouvoir faire facilement des requêtes dessus lors d’une recherche sur des critères précis (et accessoirement les stocker sous une forme qui m’apparaisse plus “propre”, simple et efficace que le vieux, complexe et verbeux format MIME)
- extraire toutes les pièces jointes directement sur le filesystem pour pouvoir y accéder directement sans passer par la recherche + ouverture du mail associé
Je crois que c’est ce que Mail.app sur MacOS fait nativement, mais je n’ai pas trouvé de bon logiciel libre qui ait cette approche (il y a bien Manitou Mail. Mais ce dernier est un peu fastidieux à installer et mettre en oeuvre alors que je voulais quelque chose de simple). C’est apparemment aussi +/- ce que différents clients mail pour Android font (e.g. GMail pour Android, k9-mail), mais je n’ai regardé que tout récemment et ne m’en suis pas inspiré à l’époque.
C’est ainsi que j’ai créé gmvaultdb. Ce dernier
- parcourt le filesystem au début d’une arborescence gmvault pour insérer les mails (HTML et TXT) dans une base sqlite3, en extrayant les pièces jointes dans un dossier à part. J’effectue une conversion en utf-8 au passage et j’insère les images directement dans le HTML (en base64) pour éviter de polluer le dossier où j’extraie les “vraies” pièces jointes
- peut faire la même opération avec un fichier mbox issue de Google Takeout (à ceci près que Google effectue une manip qui détruit les caractères non-ascii dans les mails non-utf8. Si vous avez des anciens mails non-utf8, Google Takeout n’est pas un bon outil de backup !)
- offre une petite GUI (avec QT5/PySide2 mais sans QML pour le moment) pour consulter les mails et les pièces jointes extraits et surtout effectuer des requêtes SQL de recherche
Pour la petite histoire: je me suis lancé là dedans la fleur au fusil en pensant que ce serait réglé avec “quelques” lignes de code Python (au départ je souhaitais juste retrouver quelques mails et/ou pièces jointes en particulier). C’était sans compter sur l’aspect “taquin” du format MIME lorsque des mails sont mal-formés ou avec des soucis d’encodage… (je pense que le code est toujours loin d’être parfait: je n’ai d’ailleurs pas lu en détails les RFC associées et ai surtout tâtonné jusqu’à ce que ça fonctionne chez moi, YMMV). Rappelons qu’un message MIME est constitué de différentes “parts”, qui peuvent elles-mêmes être un container MIME qui intègre des sub-parts, et ainsi de suite dans une structure récursive (il est donc logique que la fonction qui les décode soit elle-même récursive). D’après ce que j’ai observé : la plupart du temps (toujours ?), le corps du mail existe en deux versions (une en TXT et une en HTML), chacune dans une section ‘multipart/alternative’. Les images à afficher dans la version HTML sont dans un sub-part ‘multipart/related’.
Les puristes des bases de données noteront que mon schéma (simplissime : une grosse table et quelques index) est probablement loin d’être optimal (et en tout cas pas du tout 3NF) : par exemple je n’ai pas de table séparée pour les contacts, etc. ça viendra peut-être plus tard mais pour le moment j’apprécie de garder mon code simple sans devoir faire des jointures de tables. Ma priorité est plutôt d’implémenter prochainement la recherche full-text de SQLite3 (pour l’instant mes requêtes full-text se limitent aux clauses like
et glob
… Cela dit ça suffit à la majorité de mes besoins et en fait les performances de SQLite sont souvent surprenantes y compris pour des BD de plusieurs giga-octets)
Et comment on s’en sert ?
gmvaultdb createdb gmvault_backup_dir/ out_dir/
: crée dansout_dir
un fichier sqlite3mails.db
avec le contenu des emails, et un ensemble de sous-dossiers (dérivé des labels) avec les pièces jointes directement accessiblesgmvaultdb mbox mbox_file out_dir/
: même chose, mais en spécifiant un fichier MBox à la place d’une arborescence de backup GMVaultgmvaultdb gui db_file
: lance une interface graphique permettant de consulter les mails (et les pièces jointes) de la DB spécifiée. C’est encore assez moche mais c’est un premier jet qui répondait à mon besoin immédiat