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

1181 lines
35 KiB
Markdown

# 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. **Outils****Importer****Importer à 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 :
```bash
# Installer l'extension ImportExportTools NG d'abord
```
1. **Clic droit** sur un dossier dans Thunderbird
2. **ImportExportTools NG****Importer 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. **Fichier****Importer 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 :
```python
#!/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) :
```bash
# 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. **Archiver****Email****Fichiers Email**
3. Sélectionner le format **"Unix Mailbox (mbox)"**
4. Choisir le fichier et importer
#### Evolution (Linux) :
1. **Fichier****Importer**
2. Sélectionner **"Importer un seul fichier"**
3. Choisir le fichier `.mbox`
## 3. Conversion vers d'autres formats
### Vers PST (Outlook) :
```python
#!/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) :
```python
#!/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
```python
#!/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
```python
#!/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
```bash
# 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
```python
# 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
```bash
# 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 :
```python
#!/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 :
```python
#!/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 :
```python
#!/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 :
```python
#!/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** :
```python
# 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 :
```python
#!/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 :
```bash
# 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 :
```bash
# 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 :
```bash
#!/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é :
```bash
# 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 :
```python
#!/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 :
```python
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 :
```python
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 :
```bash
# Configuration dans dovecot.conf
mail_location = maildir:~/Maildir
# Test de la configuration
doveadm mailbox list -u user@domain.com
```
#### Pour Postfix + Dovecot :
```bash
# Dans main.cf
home_mailbox = Maildir/
# Redémarrage des services
systemctl restart postfix dovecot
```
#### Pour Courier :
```bash
# Courier utilise maildir par défaut
# Configuration dans /etc/courier/authdaemonrc
```
### Commandes utiles post-conversion
```bash
# 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