P
'
t
i
t
e
C
h
a
t
t
e
 
spacer~ I TOOK AN IQ TEST AND THE RESULTS WERE NEGATIVE Articles | Connexion
 
~Importer les courriers Outlook Express

Précédent  
  Python  
  Suivant
 Présentation

Vous trouverez dans ce même numéro une description du format de fichiers DBX pour Outlook Express. Nous allons employer celle-ci pour rédiger un outil de conversion des courriers de ce dernier vers le format mbox Unix.
 Sommaire

1. Intro

 Intro

Nous avons qu'un fichier DBX pour le client de courrier électronique de Microsoft se compose de trois secteurs. Parmi ceux-ci, le troisième ne présente aucun intérêt et le premier n'accueille que des informations indispensables pour localiser les données. La description proposée a mis en évidence la présence de plusieurs types d'objets, six pour être précis. Puisque Python s'avère orienté objet, nous allons tout naturellement associer une classe à chacun d'entre eux.


 Structure du programme

Notre programme va prendre la forme d'un petit script relativement simple reposant sur un module. Celui-ci contiendra toutes les classes indispensables à la manipulation des fichiers DBX. De cette manière, tout programme Python pourra réutiliser nos classes pour proposer des fonctionnalités plus variées que la simple conversion vers mbox. Ce module, nommé oe.mailbox, définira les classes dbxFileHeader, dbxFileInfo, dbxTree, dbxIndexedInfo, dbxMessageInfo et dbxMessage. Vous pourrez aisément faire les liens avec les entités présentées dans l'article du format de fichier DBX.

Souvenez-vous qu'une boîte Outlook commence par un en-tête décrivant sa nature. Cet en-tête, une instance de dbxFileHeader, propose en outre un pointeur vers un arbre, soit un objet dbxTree, contenant l'intégralité des données relatives aux emails, des objets dbxMessageInfo et dbxMessage.

Le principe consiste à fournir à chaque constructeur un flux de lecture et une adresse. Le flux correspond tout bonnement à un descripteur de fichier Python tandis que l'adresse de début de lecture proviendra des informations trouvées dans un objet différent. Par exemple, un dbxMessageInfo nous renseignera sur l'adresse d'un dbxMessage. Voyons l'implémentation de la classe dbxFileHeader.

Celle-ci s'avère particulièrement simple. Un en-tête DBX commence au premier octet d'un fichier et ne contient que des valeurs codées sur 4 octets. Il nous suffira donc de connaître la position d'une information pour en obtenir la valeur. Le listing 1 présente la définition la plus simple possible de cette classe. Nous pouvons nous attarder sur deux points, communs à toutes les autres classes : l'initialisation du buffer et la méthode getEntry(). Suivant l'objet considéré, la taille sera fixe, comme ici, ou variable. Dans le second cas, nous en serons informés par l'entremise d'un paramètre du constructeur. La création du buffer de lecture se révèle possible grâce à la méthode read() du flux qui renvoie un tableau d'octets. Voici un exemple de création d'un tel flux :

stream = open("fichier.dbx", "rb", 0)
header = dbxFileHeader(stream)
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
Ce flux servira à toutes les classes. Nous avons défini que les données de l'en-tête sont indexées. C'est pourquoi la méthode getEntry() attend en paramètre un indice. La classe que vous trouverez sur le CD-Rom propose, outre quelques constantes, un tableau établissant des correspondances entre indice d'une donnée et explications sur ladite donnée. Notre méthode doit donc créer une valeur codée sur 4 octets. Or nous possédons un buffer ne contenant que des octets. La magie s'opère par le biais de la méthode struct.unpack(). Cette dernière peut concaténer les éléments d'un tableau d'octets pour former une valeur dont la taille se trouve définie par le premier paramètre, ici "L" pour "unsigned long integer". Notez que nous récupérons en retour un tuple bien qu'il ne contienne qu'un seul élément. L'implémentation complète de dbxFileHeader propose plusieurs fonctions utiles pour déterminer la nature exacte du fichier, notamment isValid() et isMessages() qui valident le format du fichier et sa qualité de boîte de courriers.


 Chaînage des objets

Munis de notre en-tête nous devons à présent trouver l'arbre contenant les données. Le cas particulier de dbxFileInfo ne sera pas traité ici puisque cette classe ne se trouve pas indispensable à l'écriture de notre programme. Pour créer cet arbre, donc, le programmeur doit connaître son adresse au sein du fichier ainsi que le nombre d'éléments le composant. Ces deux informations s'avèrent indiquées dans l'en-tête. Trois lignes de code nous permettent d'instancier dbxTree :

entries = header.getEntry(dbxFileHeader.FH_ENTRIES)
adress = header.getEntry(dbxFileHeader.FH_TREE_ROOT_NODE_PTR)
tree = dbxTree(stream, adress, entries)
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
Le procédé de construction de l'arbre pose quelques problèmes de récursivité mais rien de particulièrement difficile. Le parcours de l'arbre s'effectue noeud par noeud. Pour chacun d'entre eux, tous les enfants se voient examinés. Le nombre d'enfants d'un noeud donné se calcule ainsi :

for i in range(entries):
  adress = tree.getValue(i)
  messageInfo = dbxMessageInfo(dbx, adress)

  messageAdress = messageInfo.getValueAsLong(dbxMessageInfo.MI_MESSAGE_ADRESS)
  message = dbxMessage(dbx, messageAdress)
       
      
JextCopier dans Jext
Les instances de dbxMessage représentent les fragments chaînés découverts dans l'article sur le format de fichier. Nous connaissons l'adresse de la tête de liste grâce au champ MI_MESSAGE_ADDRESS. Le rôle de dbxMessage consiste ainsi à parcourir toute la liste, en récupérant à chaque fois un morceau de texte qui sera accolé aux autres déjà lus. Nous obtenons au final la chaîne de caractères self.dbxText.


 Téléchargement

Le programme oemailbox contient notre bibliothèque et un programme d'exemple. Le projet Open Source SpamBayes utilise également cette bibliothèque.



par Romain Guy
romain.guy@jext.org
http://www.jext.org
Dernière mise à jour : 14/10/2006


Précédent  
  Python  
  Suivant

 
#ProgX©2005 Mathieu GINOD - Romain GUY - Erik LOUISE