Files
Maison/phpAduc/LireMBox.md
2026-02-10 12:12:11 +01:00

35 KiB

Guide d'ouverture des fichiers .mbox de Google Takeout

Ce guide explique comment ouvrir et exploiter un fichier .mbox exporté par Google Takeout.

1. Qu'est-ce qu'un fichier .mbox

Le format mbox (mailbox) est un format standard pour stocker des collections d'emails. Google Takeout exporte les emails Gmail dans ce format.

Structure du fichier :

  • Un seul fichier contenant tous les emails
  • Format texte avec séparateurs spéciaux
  • Headers et corps des emails en texte brut
  • Pièces jointes encodées en base64

⚠️ Limitations importantes concernant l'arborescence :

Ce qui est préservé :

  • Contenu des emails (headers, corps, pièces jointes)
  • Métadonnées (expéditeur, destinataire, date, sujet)
  • Labels Gmail (dans les headers comme X-Gmail-Labels)
  • Conversations (via Message-ID et In-Reply-To)

Ce qui est perdu :

  • Structure des dossiers Gmail (hiérarchie)
  • Organisation en dossiers telle qu'elle apparaît dans Gmail
  • Statuts des messages (lu/non-lu, important, etc.)
  • Tri chronologique exact des conversations

Important : Gmail utilise un système de labels plutôt que de vrais dossiers. Dans le .mbox, ces labels apparaissent comme :

X-Gmail-Labels: Important,Work,Project-Alpha

Ces labels sont inclus mais pas l'arborescence visuelle de Gmail.

2. Méthodes d'ouverture

Méthode 1 : Thunderbird (Recommandée)

Import direct :

  1. Ouvrir Thunderbird
  2. OutilsImporterImporter à partir d'un fichier
  3. Sélectionner "Fichier mbox"
  4. Choisir le fichier .mbox de Google Takeout
  5. Sélectionner le dossier de destination

Avec ImportExportTools NG :

# Installer l'extension ImportExportTools NG d'abord
  1. Clic droit sur un dossier dans Thunderbird
  2. ImportExportTools NGImporter fichier mbox
  3. Sélectionner le fichier .mbox
  4. Les emails apparaissent dans le dossier choisi

Méthode 2 : Apple Mail (macOS)

  1. Ouvrir Apple Mail
  2. FichierImporter des boîtes aux lettres
  3. Sélectionner "Fichiers au format mbox"
  4. Choisir le fichier et importer

Méthode 3 : Outils en ligne de commande

Avec Python et mailbox :

#!/usr/bin/env python3
import mailbox
import email
from email.header import decode_header

def read_mbox(mbox_path):
    """
    Lire et afficher les emails d'un fichier mbox
    """
    mbox = mailbox.mbox(mbox_path)
    
    for i, message in enumerate(mbox):
        print(f"\n=== Email {i+1} ===")
        
        # Décoder l'objet
        subject = decode_header(message['Subject'])[0][0]
        if isinstance(subject, bytes):
            subject = subject.decode('utf-8')
        print(f"Sujet: {subject}")
        
        # Expéditeur et destinataire
        print(f"De: {message['From']}")
        print(f"À: {message['To']}")
        print(f"Date: {message['Date']}")
        
        # Corps du message
        if message.is_multipart():
            for part in message.walk():
                if part.get_content_type() == "text/plain":
                    body = part.get_payload(decode=True)
                    if body:
                        print(f"Corps: {body.decode('utf-8', errors='ignore')[:200]}...")
        else:
            body = message.get_payload(decode=True)
            if body:
                print(f"Corps: {body.decode('utf-8', errors='ignore')[:200]}...")

# Utilisation
read_mbox('/path/to/gmail.mbox')

Avec mutt (Linux) :

# Installer mutt
sudo apt install mutt

# Ouvrir le fichier mbox
mutt -f /path/to/gmail.mbox

Méthode 4 : Outils graphiques

MailStore Home (Windows - Gratuit) :

  1. Télécharger MailStore Home
  2. ArchiverEmailFichiers Email
  3. Sélectionner le format "Unix Mailbox (mbox)"
  4. Choisir le fichier et importer

Evolution (Linux) :

  1. FichierImporter
  2. Sélectionner "Importer un seul fichier"
  3. Choisir le fichier .mbox

3. Conversion vers d'autres formats

Vers PST (Outlook) :

#!/usr/bin/env python3
import mailbox
import os

def mbox_to_eml(mbox_path, output_dir):
    """
    Convertir mbox vers fichiers EML individuels
    """
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    
    mbox = mailbox.mbox(mbox_path)
    
    for i, message in enumerate(mbox):
        # Créer un nom de fichier basé sur l'objet
        subject = message.get('Subject', f'Email_{i}')
        # Nettoyer le nom de fichier
        filename = "".join(c for c in subject if c.isalnum() or c in (' ', '-', '_')).rstrip()
        filename = f"{i:04d}_{filename[:50]}.eml"
        
        # Écrire le fichier EML
        with open(os.path.join(output_dir, filename), 'w', encoding='utf-8') as f:
            f.write(str(message))
        
        print(f"Exporté: {filename}")

# Utilisation
mbox_to_eml('/path/to/gmail.mbox', '/path/to/eml_output/')

Vers CSV (métadonnées) :

#!/usr/bin/env python3
import mailbox
import csv
from email.header import decode_header
from email.utils import parsedate_to_datetime

def mbox_to_csv(mbox_path, csv_path):
    """
    Exporter les métadonnées mbox vers CSV
    """
    mbox = mailbox.mbox(mbox_path)
    
    with open(csv_path, 'w', newline='', encoding='utf-8') as csvfile:
        writer = csv.writer(csvfile)
        
        # En-têtes
        writer.writerow(['Index', 'Date', 'De', 'À', 'Sujet', 'Taille'])
        
        for i, message in enumerate(mbox):
            # Décoder l'objet
            subject = message.get('Subject', '')
            if subject:
                decoded = decode_header(subject)[0][0]
                if isinstance(decoded, bytes):
                    subject = decoded.decode('utf-8', errors='ignore')
            
            # Date
            date_str = message.get('Date', '')
            try:
                date_obj = parsedate_to_datetime(date_str)
                date_formatted = date_obj.strftime('%Y-%m-%d %H:%M:%S')
            except:
                date_formatted = date_str
            
            # Taille du message
            size = len(str(message))
            
            writer.writerow([
                i + 1,
                date_formatted,
                message.get('From', ''),
                message.get('To', ''),
                subject,
                size
            ])
    
    print(f"Export CSV terminé: {csv_path}")

# Utilisation
mbox_to_csv('/path/to/gmail.mbox', '/path/to/emails_metadata.csv')

4. Extraction des pièces jointes

#!/usr/bin/env python3
import mailbox
import os
import email

def extract_attachments(mbox_path, output_dir):
    """
    Extraire toutes les pièces jointes d'un fichier mbox
    """
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    
    mbox = mailbox.mbox(mbox_path)
    attachment_count = 0
    
    for i, message in enumerate(mbox):
        if message.is_multipart():
            for part in message.walk():
                # Vérifier si c'est une pièce jointe
                if part.get_content_disposition() == 'attachment':
                    filename = part.get_filename()
                    if filename:
                        # Nettoyer le nom de fichier
                        filename = f"{i:04d}_{filename}"
                        filepath = os.path.join(output_dir, filename)
                        
                        # Sauvegarder la pièce jointe
                        with open(filepath, 'wb') as f:
                            f.write(part.get_payload(decode=True))
                        
                        print(f"Pièce jointe extraite: {filename}")
                        attachment_count += 1
    
    print(f"Total des pièces jointes extraites: {attachment_count}")

# Utilisation
extract_attachments('/path/to/gmail.mbox', '/path/to/attachments/')

5. Recherche dans le fichier mbox

#!/usr/bin/env python3
import mailbox
import re
from email.header import decode_header

def search_mbox(mbox_path, search_term, search_in=['subject', 'body', 'from']):
    """
    Rechercher dans un fichier mbox
    """
    mbox = mailbox.mbox(mbox_path)
    results = []
    
    for i, message in enumerate(mbox):
        found = False
        
        # Recherche dans l'objet
        if 'subject' in search_in:
            subject = message.get('Subject', '')
            if subject and search_term.lower() in subject.lower():
                found = True
        
        # Recherche dans l'expéditeur
        if 'from' in search_in:
            from_addr = message.get('From', '')
            if search_term.lower() in from_addr.lower():
                found = True
        
        # Recherche dans le corps
        if 'body' in search_in:
            if message.is_multipart():
                for part in message.walk():
                    if part.get_content_type() == "text/plain":
                        body = part.get_payload(decode=True)
                        if body and search_term.lower() in body.decode('utf-8', errors='ignore').lower():
                            found = True
                            break
            else:
                body = message.get_payload(decode=True)
                if body and search_term.lower() in body.decode('utf-8', errors='ignore').lower():
                    found = True
        
        if found:
            subject = decode_header(message.get('Subject', ''))[0][0]
            if isinstance(subject, bytes):
                subject = subject.decode('utf-8', errors='ignore')
            
            results.append({
                'index': i + 1,
                'subject': subject,
                'from': message.get('From', ''),
                'date': message.get('Date', '')
            })
    
    return results

# Utilisation
results = search_mbox('/path/to/gmail.mbox', 'contrat', ['subject', 'body'])
for result in results:
    print(f"Email {result['index']}: {result['subject']} - {result['from']}")

6. Outils recommandés par plateforme

Windows :

  • Thunderbird (gratuit, multiplateforme)
  • MailStore Home (gratuit pour usage personnel)
  • Outlook (avec conversion préalable)

macOS :

  • Apple Mail (intégré)
  • Thunderbird (gratuit)

Linux :

  • Thunderbird (recommandé)
  • Evolution (GNOME)
  • mutt (ligne de commande)
  • Scripts Python (pour traitement automatisé)

7. Troubleshooting

Problème : Fichier mbox corrompu

# Vérifier l'intégrité
file gmail.mbox
head -n 10 gmail.mbox

# Réparer si nécessaire (supprimer les caractères problématiques)
sed 's/\x00//g' gmail.mbox > gmail_clean.mbox

Problème : Encodage des caractères

# Forcer l'encodage UTF-8
with open('gmail.mbox', 'r', encoding='utf-8', errors='ignore') as f:
    content = f.read()

Problème : Fichier trop volumineux

# Diviser le fichier mbox
split -l 1000 gmail.mbox gmail_part_

# Chaque partie peut ensuite être importée séparément

8. Scripts utilitaires complets

Script d'analyse complète d'un fichier mbox :

#!/usr/bin/env python3
import mailbox
import os
import sys
from email.header import decode_header
from email.utils import parsedate_to_datetime
from collections import Counter
import argparse

def analyze_mbox(mbox_path):
    """
    Analyser complètement un fichier mbox
    """
    if not os.path.exists(mbox_path):
        print(f"Erreur: Le fichier {mbox_path} n'existe pas")
        return
    
    print(f"Analyse du fichier: {mbox_path}")
    print("=" * 50)
    
    mbox = mailbox.mbox(mbox_path)
    
    total_emails = 0
    total_size = 0
    senders = Counter()
    years = Counter()
    months = Counter()
    has_attachments = 0
    
    for message in mbox:
        total_emails += 1
        total_size += len(str(message))
        
        # Analyser l'expéditeur
        sender = message.get('From', 'Inconnu')
        senders[sender] += 1
        
        # Analyser la date
        date_str = message.get('Date', '')
        try:
            date_obj = parsedate_to_datetime(date_str)
            years[date_obj.year] += 1
            months[f"{date_obj.year}-{date_obj.month:02d}"] += 1
        except:
            pass
        
        # Vérifier les pièces jointes
        if message.is_multipart():
            for part in message.walk():
                if part.get_content_disposition() == 'attachment':
                    has_attachments += 1
                    break
    
    # Affichage des statistiques
    print(f"Nombre total d'emails: {total_emails}")
    print(f"Taille totale: {total_size / (1024*1024):.2f} MB")
    print(f"Emails avec pièces jointes: {has_attachments}")
    print(f"Taille moyenne par email: {total_size / total_emails / 1024:.2f} KB")
    
    print("\nTop 10 des expéditeurs:")
    for sender, count in senders.most_common(10):
        print(f"  {count:4d} - {sender}")
    
    print("\nRépartition par année:")
    for year in sorted(years.keys()):
        print(f"  {year}: {years[year]} emails")
    
    print("\nDerniers mois:")
    for month in sorted(months.keys())[-12:]:
        print(f"  {month}: {months[month]} emails")

def main():
    parser = argparse.ArgumentParser(description='Analyser un fichier mbox')
    parser.add_argument('mbox_file', help='Chemin vers le fichier mbox')
    
    args = parser.parse_args()
    analyze_mbox(args.mbox_file)

if __name__ == "__main__":
    main()

Script de conversion universelle :

#!/usr/bin/env python3
import mailbox
import os
import argparse
import json
from email.header import decode_header

def convert_mbox(mbox_path, output_format, output_dir):
    """
    Convertir un fichier mbox vers différents formats
    """
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    
    mbox = mailbox.mbox(mbox_path)
    
    if output_format == 'eml':
        convert_to_eml(mbox, output_dir)
    elif output_format == 'json':
        convert_to_json(mbox, output_dir)
    elif output_format == 'txt':
        convert_to_txt(mbox, output_dir)
    else:
        print(f"Format {output_format} non supporté")

def convert_to_eml(mbox, output_dir):
    """Convertir vers des fichiers EML individuels"""
    for i, message in enumerate(mbox):
        filename = f"{i:06d}.eml"
        with open(os.path.join(output_dir, filename), 'w', encoding='utf-8') as f:
            f.write(str(message))
        print(f"Exporté: {filename}")

def convert_to_json(mbox, output_dir):
    """Convertir vers JSON"""
    emails = []
    for i, message in enumerate(mbox):
        email_data = {
            'index': i,
            'subject': decode_header_safe(message.get('Subject', '')),
            'from': message.get('From', ''),
            'to': message.get('To', ''),
            'date': message.get('Date', ''),
            'body': extract_body(message)
        }
        emails.append(email_data)
    
    with open(os.path.join(output_dir, 'emails.json'), 'w', encoding='utf-8') as f:
        json.dump(emails, f, indent=2, ensure_ascii=False)
    print(f"Exporté: emails.json ({len(emails)} emails)")

def decode_header_safe(header):
    """Décoder un header de manière sécurisée"""
    try:
        decoded = decode_header(header)[0][0]
        if isinstance(decoded, bytes):
            return decoded.decode('utf-8', errors='ignore')
        return decoded
    except:
        return header

def extract_body(message):
    """Extraire le corps d'un message"""
    if message.is_multipart():
        for part in message.walk():
            if part.get_content_type() == "text/plain":
                body = part.get_payload(decode=True)
                if body:
                    return body.decode('utf-8', errors='ignore')
    else:
        body = message.get_payload(decode=True)
        if body:
            return body.decode('utf-8', errors='ignore')
    return ""

def main():
    parser = argparse.ArgumentParser(description='Convertir un fichier mbox')
    parser.add_argument('mbox_file', help='Fichier mbox source')
    parser.add_argument('format', choices=['eml', 'json', 'txt'], help='Format de sortie')
    parser.add_argument('output_dir', help='Répertoire de sortie')
    
    args = parser.parse_args()
    convert_mbox(args.mbox_file, args.format, args.output_dir)

if __name__ == "__main__":
    main()

9. Utilisation avec Thunderbird (Détaillée)

Import pas à pas :

  1. Télécharger ImportExportTools NG :

    https://addons.thunderbird.net/addon/importexporttools-ng/
    
  2. Installation de l'extension :

    • Outils → Modules complémentaires
    • Installer depuis un fichier
    • Redémarrer Thunderbird
  3. Import du fichier mbox :

    • Créer un nouveau dossier local
    • Clic droit → ImportExportTools NG
    • Importer fichier mbox
    • Sélectionner le fichier de Google Takeout
  4. Vérification :

    • Compter les emails importés
    • Vérifier quelques emails au hasard
    • Tester la recherche

Note importante : Le format .mbox de Google Takeout est un standard ouvert qui peut être lu par la plupart des clients email. Thunderbird reste la solution la plus simple et fiable pour ouvrir ces fichiers.

10. Reconstitution de l'arborescence Gmail

Pourquoi l'arborescence est perdue :

  1. Format historique : .mbox date des années 1970, avant les dossiers hierarchiques
  2. Gmail utilise des labels : Pas de vraie hiérarchie de dossiers
  3. Un seul fichier : Tous les emails dans un flux continu
  4. Pas de métadonnées de structure : Le format ne stocke pas l'organisation visuelle

Script pour extraire les labels Gmail :

#!/usr/bin/env python3
import mailbox
import re
from collections import defaultdict

def extract_gmail_structure(mbox_path):
    """
    Extraire la structure des labels Gmail depuis un fichier mbox
    """
    mbox = mailbox.mbox(mbox_path)
    
    folders = defaultdict(list)
    conversations = defaultdict(list)
    
    for i, message in enumerate(mbox):
        # Extraire les labels Gmail
        gmail_labels = message.get('X-Gmail-Labels', '')
        if gmail_labels:
            labels = [label.strip() for label in gmail_labels.split(',')]
            
            for label in labels:
                # Nettoyer les labels système Gmail
                clean_label = clean_gmail_label(label)
                if clean_label:
                    folders[clean_label].append({
                        'index': i,
                        'subject': message.get('Subject', ''),
                        'from': message.get('From', ''),
                        'date': message.get('Date', ''),
                        'message_id': message.get('Message-ID', '')
                    })
        
        # Identifier les conversations
        msg_id = message.get('Message-ID', '')
        in_reply_to = message.get('In-Reply-To', '')
        references = message.get('References', '')
        
        if in_reply_to or references:
            thread_id = in_reply_to or references.split()[0] if references else msg_id
            conversations[thread_id].append({
                'message_id': msg_id,
                'subject': message.get('Subject', ''),
                'index': i
            })
    
    return folders, conversations

def clean_gmail_label(label):
    """
    Nettoyer les labels système Gmail
    """
    # Labels système à ignorer
    system_labels = {
        'INBOX', 'SENT', 'DRAFT', 'SPAM', 'TRASH', 'UNREAD',
        'STARRED', 'IMPORTANT', 'CATEGORY_PERSONAL', 'CATEGORY_SOCIAL',
        'CATEGORY_PROMOTIONS', 'CATEGORY_UPDATES', 'CATEGORY_FORUMS'
    }
    
    if label in system_labels:
        return None
    
    # Décoder les labels avec caractères spéciaux
    if label.startswith('"') and label.endswith('"'):
        label = label[1:-1]
    
    return label

def generate_folder_structure(folders):
    """
    Générer une structure de dossiers lisible
    """
    print("Structure des dossiers extraite de Gmail:")
    print("=" * 50)
    
    for folder_name, emails in sorted(folders.items()):
        print(f"\n📁 {folder_name} ({len(emails)} emails)")
        
        # Afficher les 5 premiers emails du dossier
        for email in emails[:5]:
            subject = email['subject'][:50] + "..." if len(email['subject']) > 50 else email['subject']
            print(f"  📧 {subject} - {email['from']}")
        
        if len(emails) > 5:
            print(f"  ... et {len(emails) - 5} autres emails")

# Utilisation
folders, conversations = extract_gmail_structure('/path/to/gmail.mbox')
generate_folder_structure(folders)

Reconstitution pour Thunderbird :

#!/usr/bin/env python3
import mailbox
import os

def create_thunderbird_folders(mbox_path, output_dir):
    """
    Créer des fichiers mbox séparés par label pour Thunderbird
    """
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    
    mbox = mailbox.mbox(mbox_path)
    folders = {}
    
    # Créer des fichiers mbox par label
    for message in mbox:
        gmail_labels = message.get('X-Gmail-Labels', '')
        if gmail_labels:
            labels = [label.strip() for label in gmail_labels.split(',')]
            
            for label in labels:
                clean_label = clean_gmail_label(label)
                if clean_label:
                    # Créer le fichier mbox pour ce label s'il n'existe pas
                    if clean_label not in folders:
                        folder_path = os.path.join(output_dir, f"{clean_label}.mbox")
                        folders[clean_label] = mailbox.mbox(folder_path)
                    
                    # Ajouter le message au dossier approprié
                    folders[clean_label].add(message)
    
    # Fermer tous les fichiers
    for folder in folders.values():
        folder.close()
    
    print(f"Dossiers créés dans {output_dir}:")
    for folder_name in folders.keys():
        print(f"  - {folder_name}.mbox")

# Utilisation
create_thunderbird_folders('/path/to/gmail.mbox', '/path/to/thunderbird_folders/')

Alternatives pour préserver l'arborescence :

1. Export par dossier Gmail :

  • Utiliser Google Takeout en sélectionnant "Inclure tous les messages dans Mail"
  • Choisir "Exporter par dossier" si disponible

2. Scripts d'export personnalisés :

# Utiliser l'API Gmail pour exporter avec structure
from googleapiclient.discovery import build

def export_with_structure(service, user_id='me'):
    """
    Exporter avec préservation de la structure
    """
    # Lister tous les labels
    labels = service.users().labels().list(userId=user_id).execute()
    
    for label in labels['labels']:
        label_id = label['id']
        label_name = label['name']
        
        # Exporter les messages de ce label
        messages = service.users().messages().list(
            userId=user_id, 
            labelIds=[label_id]
        ).execute()
        
        # Créer un fichier mbox pour ce label
        # ... code d'export ...

Recommandations pratiques :

Pour une migration complète :

  1. Utiliser l'API Gmail directement plutôt que Takeout
  2. Exporter par labels avec scripts personnalisés
  3. Documenter la structure avant export
  4. Reconstituer manuellement les dossiers importants

Pour l'archivage simple :

  1. Accepter la perte de structure pour l'archivage légal
  2. Utiliser les labels dans les métadonnées pour retrouver les emails
  3. Créer un index des emails importants avec leurs labels

11. Conversion mbox vers Maildir (format Dovecot)

Le format Maildir est utilisé par de nombreux serveurs de messagerie modernes comme Dovecot, Postfix, et Courier. Contrairement au format mbox (un seul fichier), Maildir stocke chaque email dans un fichier séparé.

Avantages du format Maildir :

  • Sécurité : Pas de corruption si un email est corrompu
  • Performance : Accès plus rapide aux emails individuels
  • Concurrence : Plusieurs processus peuvent accéder simultanément
  • Fiabilité : Moins de risques de perte de données

Structure Maildir :

Maildir/
├── cur/           # Messages lus
├── new/           # Messages non lus
├── tmp/           # Messages temporaires
└── .Subfolder/    # Sous-dossiers (avec point en préfixe)
    ├── cur/
    ├── new/
    └── tmp/

Méthode 1 : Avec Python (Script personnalisé)

Script de conversion complet :

#!/usr/bin/env python3
import mailbox
import os
import time
import email.utils
import hashlib
import sys
from pathlib import Path

def mbox_to_maildir(mbox_path, maildir_path):
    """
    Convertir un fichier mbox vers le format Maildir
    """
    # Créer la structure Maildir
    create_maildir_structure(maildir_path)
    
    # Ouvrir le fichier mbox source
    mbox = mailbox.mbox(mbox_path)
    maildir = mailbox.Maildir(maildir_path)
    
    print(f"Conversion de {mbox_path} vers {maildir_path}")
    print("=" * 50)
    
    converted_count = 0
    error_count = 0
    
    for i, message in enumerate(mbox):
        try:
            # Ajouter le message au Maildir
            key = maildir.add(message)
            converted_count += 1
            
            if converted_count % 100 == 0:
                print(f"Converti {converted_count} messages...")
        
        except Exception as e:
            print(f"Erreur pour le message {i}: {e}")
            error_count += 1
    
    print(f"\nConversion terminée:")
    print(f"  Messages convertis: {converted_count}")
    print(f"  Erreurs: {error_count}")
    print(f"  Maildir créé dans: {maildir_path}")

def create_maildir_structure(maildir_path):
    """
    Créer la structure de base d'un Maildir
    """
    Path(maildir_path).mkdir(parents=True, exist_ok=True)
    Path(maildir_path, 'cur').mkdir(exist_ok=True)
    Path(maildir_path, 'new').mkdir(exist_ok=True)
    Path(maildir_path, 'tmp').mkdir(exist_ok=True)

def mbox_to_maildir_with_labels(mbox_path, base_maildir_path):
    """
    Convertir mbox vers Maildir en créant des sous-dossiers par label Gmail
    """
    mbox = mailbox.mbox(mbox_path)
    
    # Analyser les labels d'abord
    labels_map = analyze_gmail_labels(mbox_path)
    
    # Créer le Maildir principal
    create_maildir_structure(base_maildir_path)
    main_maildir = mailbox.Maildir(base_maildir_path)
    
    # Créer les sous-dossiers Maildir pour chaque label
    label_maildirs = {}
    for label in labels_map.keys():
        if label and label not in ['INBOX', 'SENT', 'DRAFT']:
            label_path = os.path.join(base_maildir_path, f'.{label}')
            create_maildir_structure(label_path)
            label_maildirs[label] = mailbox.Maildir(label_path)
    
    print(f"Conversion avec labels vers {base_maildir_path}")
    print("=" * 50)
    
    # Convertir les messages
    for i, message in enumerate(mbox):
        try:
            # Ajouter au Maildir principal
            main_maildir.add(message)
            
            # Extraire les labels Gmail
            gmail_labels = message.get('X-Gmail-Labels', '')
            if gmail_labels:
                labels = [label.strip() for label in gmail_labels.split(',')]
                
                for label in labels:
                    clean_label = clean_gmail_label(label)
                    if clean_label and clean_label in label_maildirs:
                        # Ajouter aussi au sous-dossier correspondant
                        label_maildirs[clean_label].add(message)
            
            if (i + 1) % 100 == 0:
                print(f"Converti {i + 1} messages...")
        
        except Exception as e:
            print(f"Erreur pour le message {i}: {e}")
    
    # Fermer tous les Maildir
    main_maildir.close()
    for md in label_maildirs.values():
        md.close()
    
    print(f"\nConversion terminée avec {len(label_maildirs)} sous-dossiers")

def analyze_gmail_labels(mbox_path):
    """
    Analyser les labels Gmail présents dans le mbox
    """
    mbox = mailbox.mbox(mbox_path)
    labels_count = {}
    
    for message in mbox:
        gmail_labels = message.get('X-Gmail-Labels', '')
        if gmail_labels:
            labels = [label.strip() for label in gmail_labels.split(',')]
            for label in labels:
                clean_label = clean_gmail_label(label)
                if clean_label:
                    labels_count[clean_label] = labels_count.get(clean_label, 0) + 1
    
    return labels_count

def clean_gmail_label(label):
    """
    Nettoyer les labels Gmail pour les noms de dossiers
    """
    system_labels = {
        'INBOX', 'SENT', 'DRAFT', 'SPAM', 'TRASH', 'UNREAD',
        'STARRED', 'IMPORTANT', 'CATEGORY_PERSONAL', 'CATEGORY_SOCIAL',
        'CATEGORY_PROMOTIONS', 'CATEGORY_UPDATES', 'CATEGORY_FORUMS'
    }
    
    if label in system_labels:
        return None
    
    # Nettoyer le nom pour le système de fichiers
    clean = label.replace('"', '').replace('/', '_').replace(' ', '_')
    return clean if clean else None

def main():
    if len(sys.argv) < 3:
        print("Usage: python3 mbox2maildir.py <fichier.mbox> <dossier_maildir> [--with-labels]")
        sys.exit(1)
    
    mbox_path = sys.argv[1]
    maildir_path = sys.argv[2]
    with_labels = len(sys.argv) > 3 and sys.argv[3] == '--with-labels'
    
    if not os.path.exists(mbox_path):
        print(f"Erreur: Le fichier {mbox_path} n'existe pas")
        sys.exit(1)
    
    if with_labels:
        mbox_to_maildir_with_labels(mbox_path, maildir_path)
    else:
        mbox_to_maildir(mbox_path, maildir_path)

if __name__ == "__main__":
    main()

Méthode 2 : Avec mb2md (outil spécialisé)

Installation :

# Sur Ubuntu/Debian
sudo apt install mb2md

# Sur CentOS/RHEL
sudo yum install mb2md

# Ou téléchargement manuel
wget http://batleth.sapienti-sat.org/projects/mb2md/mb2md-3.20.pl
chmod +x mb2md-3.20.pl

Utilisation :

# Conversion simple
mb2md -s /path/to/gmail.mbox -d /path/to/maildir

# Avec création de sous-dossiers
mb2md -s /path/to/gmail.mbox -d /path/to/maildir -R

# Verbose pour voir le progrès
mb2md -s /path/to/gmail.mbox -d /path/to/maildir -v

Méthode 3 : Avec formail et procmail

Script bash utilisant formail :

#!/bin/bash
# Convertir mbox vers Maildir avec formail

MBOX_FILE="$1"
MAILDIR_PATH="$2"

if [ -z "$MBOX_FILE" ] || [ -z "$MAILDIR_PATH" ]; then
    echo "Usage: $0 <fichier.mbox> <dossier_maildir>"
    exit 1
fi

# Créer la structure Maildir
mkdir -p "$MAILDIR_PATH"/{cur,new,tmp}

echo "Conversion de $MBOX_FILE vers $MAILDIR_PATH"

# Utiliser formail pour séparer les messages
cat "$MBOX_FILE" | formail -ds sh -c '
    # Générer un nom de fichier unique
    TIMESTAMP=$(date +%s)
    RANDOM_NUM=$RANDOM
    HOSTNAME=$(hostname)
    FILENAME="${TIMESTAMP}.${RANDOM_NUM}.${HOSTNAME}"
    
    # Sauvegarder le message dans new/
    cat > "'$MAILDIR_PATH'/new/$FILENAME"
    echo "Message sauvé: $FILENAME"
'

echo "Conversion terminée"
echo "Messages dans: $MAILDIR_PATH/new/"
ls -1 "$MAILDIR_PATH/new/" | wc -l | xargs echo "Nombre de messages:"

Méthode 4 : Avec Dovecot dsync

Si Dovecot est installé :

# Créer une configuration temporaire Dovecot
cat > /tmp/dovecot-convert.conf << EOF
mail_location = mbox:~/mbox:INBOX=/path/to/gmail.mbox
namespace inbox {
  inbox = yes
  location = 
  mailbox Drafts {
    special_use = \Drafts
  }
  mailbox Junk {
    special_use = \Junk
  }
  mailbox Sent {
    special_use = \Sent
  }
  mailbox "Sent Messages" {
    special_use = \Sent
  }
  mailbox Trash {
    special_use = \Trash
  }
  prefix = 
}
EOF

# Conversion avec dsync
doveadm -c /tmp/dovecot-convert.conf backup -u user@domain.com maildir:/path/to/output/maildir

Vérification de la conversion

Script de vérification :

#!/usr/bin/env python3
import mailbox
import os

def verify_conversion(mbox_path, maildir_path):
    """
    Vérifier que la conversion mbox -> maildir est correcte
    """
    print("Vérification de la conversion")
    print("=" * 30)
    
    # Compter les messages dans le mbox original
    mbox = mailbox.mbox(mbox_path)
    mbox_count = len(mbox)
    print(f"Messages dans mbox: {mbox_count}")
    
    # Compter les messages dans le Maildir
    maildir = mailbox.Maildir(maildir_path)
    maildir_count = len(maildir)
    print(f"Messages dans Maildir: {maildir_count}")
    
    # Vérifier la structure
    cur_count = len(os.listdir(os.path.join(maildir_path, 'cur')))
    new_count = len(os.listdir(os.path.join(maildir_path, 'new')))
    print(f"  - Dans cur/: {cur_count}")
    print(f"  - Dans new/: {new_count}")
    
    # Résultat
    if mbox_count == maildir_count:
        print("✅ Conversion réussie!")
    else:
        print(f"❌ Différence détectée: {mbox_count - maildir_count} messages")
    
    return mbox_count == maildir_count

# Utilisation
verify_conversion('/path/to/gmail.mbox', '/path/to/maildir')

Optimisations et bonnes pratiques

Pour de gros fichiers mbox :

def mbox_to_maildir_chunked(mbox_path, maildir_path, chunk_size=1000):
    """
    Conversion par chunks pour économiser la mémoire
    """
    mbox = mailbox.mbox(mbox_path)
    maildir = mailbox.Maildir(maildir_path)
    
    count = 0
    for message in mbox:
        maildir.add(message)
        count += 1
        
        if count % chunk_size == 0:
            print(f"Traité {count} messages...")
            # Forcer l'écriture
            maildir.flush()
    
    maildir.close()
    print(f"Conversion terminée: {count} messages")

Préservation des métadonnées :

def preserve_metadata(message, maildir_path, filename):
    """
    Préserver les métadonnées lors de la conversion
    """
    # Extraire la date du message
    date_str = message.get('Date', '')
    if date_str:
        try:
            import email.utils
            timestamp = email.utils.mktime_tz(email.utils.parsedate_tz(date_str))
            
            # Appliquer la date au fichier
            filepath = os.path.join(maildir_path, 'new', filename)
            os.utime(filepath, (timestamp, timestamp))
        except:
            pass

Utilisation avec différents serveurs

Pour Dovecot :

# Configuration dans dovecot.conf
mail_location = maildir:~/Maildir

# Test de la configuration
doveadm mailbox list -u user@domain.com

Pour Postfix + Dovecot :

# Dans main.cf
home_mailbox = Maildir/

# Redémarrage des services
systemctl restart postfix dovecot

Pour Courier :

# Courier utilise maildir par défaut
# Configuration dans /etc/courier/authdaemonrc

Commandes utiles post-conversion

# Statistiques du Maildir
find /path/to/maildir -name "*" -type f | wc -l

# Vérifier l'intégrité
for dir in cur new tmp; do
    echo "$dir: $(ls /path/to/maildir/$dir | wc -l) messages"
done

# Indexation Dovecot (si applicable)
doveadm index -u user@domain.com INBOX

# Reconstruction des index
doveadm force-resync -u user@domain.com '*'

Date de création : 22 octobre 2025