angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

IMAPClient et mise à plat de la BODYSTRUCTURE

L'aplatissement de la BODYSTRUCTURE nécessite que vous commenciez à lire la RFC3501.

27 septembre 2021 Mise à jour 27 septembre 2021
Dans Email
post main image
https://unsplash.com/@2hmedia

Les développeurs d'applications veulent utiliser des solutions éprouvées pour créer une application. Cela fonctionne souvent, mais avec le paquet IMAPClient , il manque un certain nombre de choses.

Le principe de IMAP est d'obtenir uniquement ce que vous demandez. Supposons que vous ayez un e-mail contenant de nombreuses pièces jointes et que vous souhaitiez n'en visualiser ou télécharger qu'une seule. Pour ce faire, vous avez besoin de la "body_number" de cette pièce jointe, puis de la FETCH de cette partie.

Sur internet, vous voyez des gens qui téléchargent le message entier mais ce n'est pas la bonne façon de faire ! Je vous présente ici une solution pour obtenir le body_numbers de toutes les parties d'un message électronique.

Aplatir le BODYSTRUCTURE

J'ai moi-même eu du mal à résoudre ce problème et, pour commencer, j'ai utilisé un code que j'ai trouvé sur Internet, mais son utilité était limitée. Il est temps de commencer à lire le RFC3501, voir les liens ci-dessous. Un message électronique IMAP n'est pas seulement composé de pièces jointes comme les fichiers images et PDF , mais les pièces jointes peuvent aussi être des messages eux-mêmes, ce qui signifie que notre code doit utiliser la récursion.

Ce que je veux, c'est une liste de body_numbers qui peut être utilisée pour FETCH la ou les pièces que nous voulons. Nous pouvons appeler cette opération "aplatir les BODYSTRUCTURE" car nous passons des récursions à une liste. Au cours de ce processus, nous générons la body_numbers.

MULTIPART/ALTERNATIVE, MULTIPART/MIXED, MULTIPART/RELATED, ...

Les messages électroniques sont la plupart du temps construits avec des éléments qui sont liés les uns aux autres. Il existe différents types de relations, par exemple :

  • ALTERNATIVE : les parties en alternance ont le même contenu, de sorte que le client de messagerie peut choisir celle à montrer
  • RELATED : les parties doivent être présentées ensemble, et non pas alternativement. Par conséquent, elles sont combinées. Par exemple, image en ligne
  • MIXED : les parties contiennent des informations différentes et ne sont pas censées être présentées ensemble.

Le document 'IMAP BODYSTRUCTURE: formatted examples' donne une bonne introduction, voir les liens ci-dessous.

Voici un exemple de relation ALTERNATIVE . Le BODYSTRUCTURE renvoyé par IMAPClient est :

(
    [
        (b'text', b'plain', (b'charset', b'iso-8859-1'), None, None, b'quoted-printable', 426, 15, None, None, None, None), 
        (b'text', b'html', (b'charset', b'iso-8859-1'), None, None, b'quoted-printable', 1085, 36, None, None, None, None)
    ], 
    b'alternative', 
    (b'boundary', b'_000_CWXP265MB4244C3FA1F3563A988AAE2CABBDF9CWXP265MB4244GBRP_'), 
    None, 
    (b'en-US',), 
    None
)

IMAPClient nous donne une liste d'éléments ALTERNATIVE . Nous pouvons choisir d'afficher soit le texte brut, soit la partie HTML . Les body_parts aplaties sont, le premier numéro est le body_number :

1        - ALTERNATIVE : TEXT, text/plain, iso-8859-1
2        - ALTERNATIVE : TEXT, text/html, iso-8859-1

Voici le même exemple avec un accessoire PDF ajouté :

(
    [
        (
            [
                (b'text', b'plain', (b'charset', b'UTF-8'), None, None, b'8bit', 1268, 25, None, (b'inline', None), None, None), 
                (b'text', b'html', (b'charset', b'UTF-8'), None, None, b'8bit', 10887, 115, None, (b'inline', None), None, None)
            ], 
            b'alternative', 
            (b'boundary', b'alt-60e413d9174229.65052373'), 
            None,
            None,
            None
        ), 
        (b'application', b'pdf', (b'name', b'summary.pdf'), None, None, b'base64', 187354, None, 
            (b'attachment', (b'filename', b'summary.pdf')), None, None)
    ], 
    b'mixed', 
    (b'boundary', b'multipart-60e413d9174190.84932644'), 
    None, 
    None, 
    None
)

Les body_parts aplaties sont, le premier numéro est le body_number :

1.1      - ALTERNATIVE : TEXT, text/plain, utf-8
1.2      - ALTERNATIVE : TEXT, text/html, utf-8
2        - MIXED       : NON_MULTIPART-ATTACHMENT, application/pdf, summary.pdf

D'où viennent les body_numbers ?

Nous ne recevons pas les body_numbers du serveur IMAP . Au lieu de cela, nous obtenons la BODYSTRUCTURE du serveur IMAP et devons générer la body_numbers à partir de celle-ci. Dans les exemples ci-dessus, ce n'est pas difficile. Mais cela devient plus compliqué avec les messages joints.

La structure de la BODYSTRUCTURE

Pour comprendre la BODYSTRUCTURE , j'ai copié du texte de la RFC3501 :

Les champs de base d'une body part non multipartite sont dans l'ordre suivant :

  • body type
    Une chaîne donnant le nom du type de média de contenu tel que défini dans [MIME-IMB].
  • body subtype
    Chaîne donnant le nom du sous-type de contenu tel que défini dans [MIME-IMB].

  • body parameter parenthesized list
    Liste entre parenthèses de paires attribut/valeur [par exemple, ("foo" "bar" "baz" "rag") où "bar" est la valeur de "foo" et "rag" est la valeur de "baz"] telles que définies dans [MIME-IMB].

  • body id
    Chaîne donnant l'identifiant du contenu tel que défini dans [MIME-IMB].

  • body description
    Une chaîne donnant le contenu de tel que défini dans [MIME-IMB].

  • body encoding
    Chaîne donnant l'encodage de transfert du contenu tel que défini dans [MIME-IMB].

  • body size
    Un nombre donnant la taille du corps en octets. Notez que cette taille est la taille dans son codage de transfert et non la taille résultante après tout décodage.

Un body type de type MESSAGE et de sous-type RFC822 contient, immédiatement après les champs de base, la structure de l'enveloppe, la structure du corps et la taille en lignes du message encapsulé.

Un body type de type TEXT contient, immédiatement après les champs de base, la taille du corps en lignes de texte. Il convient de noter que cette taille correspond à la taille dans son codage de transfert de contenu et non à la taille obtenue après décodage.

Les données d'extension suivent les champs de base et les champs spécifiques au type énumérés ci-dessus. Les données d'extension ne sont jamais renvoyées avec l'extraction BODY, mais peuvent l'être avec une extraction BODYSTRUCTURE . Les données d'extension, si elles sont présentes, DOIVENT être dans l'ordre défini.

Notez que le body type de type MESSAGE et de sous-type RFC822 introduit une récursion. Lorsque nous rencontrons cela, nous traitons ce message comme un nouveau message, nous extrayons les body_number, etc. Et ce message peut à nouveau contenir un ou plusieurs autres messages.

Selon la RFC3501, chaque message contient une structure d'enveloppe, et une structure de corps. La structure de corps du message est la nouvelle structure de corps que nous devons traiter.

Exemple de messages imbriqués

Voici un exemple d'un message qui contient un autre message qui contient un autre message avec une pièce jointe. Le BODYSTRUCTURE renvoyé par le IMAPClient est :

(
    [
        (b'text', b'plain', (b'charset', b'utf-8'), None, None, b'7bit', 35, 7, None, None, None, None), 
        (b'message', b'rfc822', (b'name', b'Re: My message.eml'), None, None, b'7bit', 10034116, 
            (
                b'Sun, 19 Sep 2021 10:04:43 +0200', 
                b'Re: My message', 
                ((b'Bob Smith', None, b'bobsmith', b'example.com'),), 
                ((b'Bob Smith', None, b'bobsmith', b'example.com'),), 
                ((b'Bob Smith', None, b'bobsmith', b'example.com'),), 
                ((b'richardroe@example.org', None, b'richardroe', b'example.org'),), 
                None, 
                None, 
                None, 
                b'<8b678e28-d03a-2bdd-2930-12470235ef9a@example.com>'
            ), 
            (
                (b'text', b'plain', (b'charset', b'utf-8'), None, None, b'7bit', 46, 7, None, None, None, None), 
                (b'message', b'rfc822', (b'name', b'Fw: Some email two.eml'), None, None, b'7bit', 10029135, 
                    (
                        b'Sat, 18 Sep 2021 18:35:47 +0200', 
                        b'Fw: Some email two', 
                        ((b'John Doe', None, b'johndoe', b'example.org'),), 
                        ((b'John Doe', None, b'johndoe', b'example.org'),), 
                        ((b'John Doe', None, b'johndoe', b'example.org'),), 
                        ((None, None, b'richardroe', b'example.org'),), 
                        None,
                        None,
                        None,
                        b'<a7dfbf41-1a26-4316-b8b5-1753fc17cd54-1631982946791@3c-example.org>'), 
                    (
                        (b'text', b'html', (b'charset', b'UTF-8'), None, None, b'7bit', 5053, 89, None, None, None, None),
                        (b'application', b'pdf', (b'name', b'YZ345.pdf'), None, None, b'base64', 187354, None, 
                            (b'attachment', (b'filename', b'YZ345.pdf')), None, None),
                        b'mixed', 
                        (b'boundary', b'abmob-49888c7b-3a09-4d10-b119-df9366de9f4c'), 
                        None, 
                        None, 
                        None
                    ), 
                    128656,
                    None,
                    (b'attachment', (b'filename', b'Fw: Some email two.eml')), 
                    None, 
                    None
                ), 
                b'mixed', 
                (b'boundary', b'------------3F95A42110AABF9E24EC86EB'), 
                None, 
                (b'en-US',), 
                None
            ), 
            128759, 
            None, 
            (b'attachment', (b'filename', b'Re: My message.eml')), None, None
        )
    ], 
    b'mixed', 
    (b'boundary', b'------------7323DBBF0E22BDA4B95E42D1'), 
    None, 
    (b'en-US',), 
    None
)

Les body_parts aplaties sont, le premier numéro est le body_number :

1        - MIXED       : TEXT, text/plain, utf-8
2        - MIXED       : MESSAGE_RFC822, message/rfc822, Re: My message.eml
2.1      - MIXED       : TEXT, text/plain, utf-8
2.2      - MIXED       : MESSAGE_RFC822, message/rfc822, Fw: Some email two.eml
2.2.1    - MIXED       : TEXT, text/html, utf-8
2.2.2    - MIXED       : NON_MULTIPART-ATTACHMENT, application/pdf, YZ345.pdf

Correction de la BODYSTRUCTURE des messages imbriqués

Lorsque vous regardez l'exemple de messages imbriqués ci-dessus, vous remarquerez que les messages imbriqués ne contiennent pas de listes, comme le message de niveau supérieur. Je considère cela comme un bogue, mais il peut s'agir d'une décision de conception.

Quoi qu'il en soit, IMAPClient contient une classe que nous pouvons utiliser pour convertir le message imbriqué BODYSTRUCTURE en un message de niveau supérieur BODYSTRUCTURE.

    body_data = BodyData()
    body_structure = body_data.create(nested_message_body_structure_part)

Maintenant, le message imbriqué BODYSTRUCTURE contient des listes et peut être traité de la même manière que le message de niveau supérieur BODYSTRUCTURE.

La classe BodyStructurePart et les types de pièces

L'aplatissement signifie que nous créons une liste d'objets BodyStructurePart. Une classe BodyStructurePart contient toutes les informations nécessaires à la poursuite du traitement. Elle doit au moins contenir les éléments suivants :

  • body_number : le body_number
  • type_corps : ALTERNATIVE, MIXED, etc.
  • body_part : la partie réelle de la BODYSTRUCTURE

En outre, j'ai ajouté des attributs :

  • part_type
  • part_subtype
  • content_type

Un part_type peut être, voir aussi RFC3501 ci-dessus :

  • MESSAGE_RFC822
  • TEXT
  • NON_MULTIPART

Un part_subtype, utilisé avec part_type = NON_MULTIPART, peut être :

  • ATTACHMENT
  • INLINE
  • OTHER

Le code du parseur BODYSTRUCTURE

Et enfin, voici le code du parseur. Il contient trois classes :

  • IMAPBodyStructurePartUtils
  • IMAPBodyStructurePart
  • IMAPBodyStructureParser

La classe IMAPBodyStructureParser possède une méthode parse qui est appelée avec la BODYSTRUCTURE retournée par IMAPClient. Cette méthode renvoie une liste d'objets IMAPBodyStructurePart qui peuvent être utilisés dans notre code.

J'ai mis le code et les exemples dans un seul fichier au cas où vous voudriez l'essayer :

import sys

from imapclient import IMAPClient
from imapclient.response_types import BodyData


class IMAPBodyStructurePartUtils:

    @classmethod
    def __decode(cls, s):
        try:
            s = s.decode()
        except Exception as e:
            pass
        return s

    @classmethod
    def get_part_type_and_part_subtype(cls, body_part):
        part_type = None
        part_subtype = None
        try:
            if body_part[0] == b'message' and body_part[1] == b'rfc822':
                part_type = 'MESSAGE_RFC822'
            elif body_part[0] == b'text':
                part_type = 'TEXT'
            else:
                part_type = 'NON_MULTIPART'
        except:
            pass
        if part_type == 'NON_MULTIPART':
            try:
                if body_part[8][0] == b'attachment':
                    part_subtype = 'ATTACHMENT'
                elif body_part[8][0] == b'inline':
                    part_subtype = 'INLINE'
                else:
                    part_subtype = 'OTHER'
            except Exception as e:
                pass
        return part_type, part_subtype

    @classmethod
    def get_content_type(cls, body_part):
        try:
            ctype = cls.__decode(body_part[0])
            csubtype = cls.__decode(body_part[1])
            return ctype.lower() + '/' + csubtype.lower()
        except Exception as e:
            pass
        return None  

    @classmethod
    def __get_charset_or_name(cls, charset_or_name, body_part):
        try:
            for a in range(0, len(body_part[2]), 2):
                key = body_part[2][a].lower()
                val = body_part[2][a + 1]
                if key == charset_or_name:
                    if charset_or_name == b'charset':
                        return cls.__decode(val).lower()
                    return cls.__decode(val)
        except Exception as e:
            pass
        return None  

    @classmethod
    def get_charset(cls, body_part):
        return cls.__get_charset_or_name(b'charset', body_part)

    @classmethod
    def get_name(cls, body_part):
        return cls.__get_charset_or_name(b'name', body_part)

    @classmethod
    def get_filename(cls, body_part):
        try:
            sub_body_part = body_part[8][1]
            for i in range(0, len(sub_body_part), 2):
                key = sub_body_part[i]
                val = sub_body_part[i + 1]
                if key == b'filename':
                    return cls.__decode(val)
        except:
            pass
        return None


class IMAPBodyStructurePart:

    def __init__(
        self,
        body_number=None,
        body_type=None,
        body_part=None,
    ):
        self.body_number = body_number
        self.body_type = body_type
        self.body_part = body_part

        self.part_type, self.part_subtype = IMAPBodyStructurePartUtils.get_part_type_and_part_subtype(self.body_part)
        self.content_type = IMAPBodyStructurePartUtils.get_content_type(self.body_part)
        self.name = IMAPBodyStructurePartUtils.get_name(self.body_part)
        self.charset = IMAPBodyStructurePartUtils.get_charset(self.body_part)
        self.filename = IMAPBodyStructurePartUtils.get_filename(self.body_part)

    def __str__(self):
        if self.body_type is None:
            self.body_type = ''
        if self.part_type == 'MESSAGE_RFC822':
            return '{:8} - {:12}: {}, {}, {}'.format(self.body_number, self.body_type, self.part_type, self.content_type, self.name)
        elif self.part_type == 'TEXT':
            return '{:8} - {:12}: {}, {}, {}'.format(self.body_number, self.body_type, self.part_type, self.content_type, self.charset)
        return '{:8} - {:12}: {}-{}, {}, {}'.format(self.body_number, self.body_type, self.part_type, self.part_subtype, self.content_type, self.filename)


class IMAPBodyStructureParser:

    def __init__(
        self,
        dbg=False,
    ):
        pass

    @classmethod
    def __is_multipart(cls, part):
        return isinstance(part[0], list)

    @classmethod
    def __get_body_type(cls, part):
        # body_type (ALTERNATIVE, MIXED, ...) is first item after list
        body_type = None
        if len(part) > 1:
            body_type = part[1]
            if body_type is not None:
                try:
                    body_type = body_type.decode()
                except Exception as e:
                    pass
        if body_type is not None:
            body_type = body_type.upper()
        return body_type

    @classmethod
    def __add_body_part(cls, body_parts, body_number, body_type, part):
        body_parts.append(IMAPBodyStructurePart(
            body_number=body_number,
            body_type=body_type,
            body_part=part,
        ))

    @classmethod
    def parse(cls, part, body_number='', body_type=None):
        return cls.__recursive_parse(body_parts=[], part=part, body_number=body_number, body_type=body_type)

    @classmethod
    def __recursive_parse(cls, body_parts, part, body_number='', body_type=None):
        if part is None:
            return None

        part_type, part_sub_type = IMAPBodyStructurePartUtils.get_part_type_and_part_subtype(part)
        if part_type == 'MESSAGE_RFC822':
            cls.__add_body_part(body_parts, body_number, body_type, part)
            # convert message body_structure at part[8] using BodyData
            body_data = BodyData()
            part = body_data.create(part[8])
            if cls.__is_multipart(part):
                body_type = cls.__get_body_type(part)
                for i, p in enumerate(part[0], 1):
                    if body_number == '':
                        next_body_number = str(i)
                    else:
                        next_body_number = body_number + '.' + str(i)
                    cls.__recursive_parse(body_parts, p, body_number=next_body_number, body_type=body_type)
            else:
                cls.__add_body_part(body_parts, body_number, body_type, part)

        elif cls.__is_multipart(part):
            body_type = cls.__get_body_type(part)
            for i, p in enumerate(part[0], 1):
                if body_number == '':
                    next_body_number = str(i)
                else:
                    next_body_number = body_number + '.' + str(i)
                cls.__recursive_parse(body_parts, p, body_number=next_body_number, body_type=body_type)
        else:
            if body_number == '':
                body_number = '1'
            cls.__add_body_part(body_parts, body_number, body_type, part)

        return body_parts


# BODYSTRUCTURE examples
body_structures = [
    
    {
        'name': 'single text/plain part',
        'body_structure': 
        (b'text', b'plain', (b'charset', b'utf-8'), None, None, b'7bit', 1148, 59, None, None, None, None),
    },
    {
        'name': 'text/plain and text/html',
        'body_structure': 
        (
            [
                (b'text', b'plain', (b'charset', b'iso-8859-1'), None, None, b'quoted-printable', 426, 15, None, None, None, None), 
                (b'text', b'html', (b'charset', b'iso-8859-1'), None, None, b'quoted-printable', 1085, 36, None, None, None, None)
            ], 
            b'alternative', 
            (b'boundary', b'_000_CWXP265MB4244C3FA1F3563A988AAE2CABBDF9CWXP265MB4244GBRP_'), 
            None, 
            (b'en-US',), 
            None
        ),
    },
    {
        'name': 'text/plain and pdf attachment',
        'body_structure': 
        (
            [
                (b'text', b'plain', (b'charset', b'ISO-8859-15'), None, None, b'quoted-printable', 394, 13, None, (b'inline', None), None, None), 
                (b'application', b'pdf', (b'name', b'manual.pdf'), None, None, b'base64', 175098, None, (b'attachment', (b'filename', b'manual.pdf')), None, None)
            ], 
            b'mixed', 
            (b'boundary', b'_----------=_1631938636414264302'), 
            None, 
            None, 
            None
        ),
    },
    {
        'name': 'text/plain and text/html and pdf attachment',
        'body_structure': 
        (
            [
                (
                    [
                        (b'text', b'plain', (b'charset', b'UTF-8'), None, None, b'8bit', 1268, 25, None, (b'inline', None), None, None), 
                        (b'text', b'html', (b'charset', b'UTF-8'), None, None, b'8bit', 10887, 115, None, (b'inline', None), None, None)
                    ], 
                    b'alternative', 
                    (b'boundary', b'alt-60e413d9174229.65052373'), 
                    None, 
                    None, 
                    None
                ), 
                (b'application', b'pdf', (b'name', b'summary.pdf'), None, None, b'base64', 187354, None, 
                    (b'attachment', (b'filename', b'summary.pdf')), None, None)
            ], 
            b'mixed', 
            (b'boundary', b'multipart-60e413d9174190.84932644'), 
            None, 
            None, 
            None
        ),
    },
    {
        'name': 'text/plain, text/html and inline image',
        'body_structure': 
        (
            [
                (
                    [
                        (b'text', b'plain', (b'charset', b'UTF-8'), None, None, b'quoted-printable', 3909, 138, None, None, None, None), 
                        (b'text', b'html', (b'charset', b'UTF-8'), None, None, b'quoted-printable', 21375, 397, None, None, None, None)
                    ], 
                    b'alternative', 
                    (b'boundary', b'000000000000239fe505c86b594b'), 
                    None, 
                    None, 
                    None
                ), 
                (b'image', b'png', (b'name', b'image.png'), b'<17afcc25c9bcb971f161>', None, b'base64', 1491868, None, 
                    (b'inline', (b'filename', b'image.png')), None, None)
            ], 
            b'related', 
            (b'boundary', 
            b'000000000000239fe605c86b594c'), 
            None, 
            None, 
            None
        ),
    },
    {
        'name': 'text/plain, text/html and inline images and pdf attachment',
        'body_structure': 
        (
            [
                (
                    [
                        (
                            [
                                (b'text', b'plain', (b'charset', b'UTF-8'), None, None, b'quoted-printable', 4393, 90, None, None, None, None), 
                                (b'text', b'html', (b'charset', b'UTF-8'), None, None, b'quoted-printable', 12720, 264, None, None, None, None)
                            ], 
                            b'alternative', 
                            (b'boundary', b'0000000000007dda0f05c7b3e2d2'), 
                            None, 
                            None, 
                            None
                        ),
                        (b'image', b'png', (b'name', b'image.png'), b'<17acdbf96cccb971f161>', None, b'base64', 120514, None, 
                            (b'inline', (b'filename', b'image.png')), None, None), 
                        (b'image', b'png', (b'name', b'image.png'), b'<17acdbf96cccb971f162>', None, b'base64', 78208, None, 
                            (b'inline', (b'filename', b'image.png')), None, None)
                    ], 
                    b'related', 
                    (b'boundary', b'0000000000007dda1005c7b3e2d3'), 
                    None, 
                    None, 
                    None
                ), 
                (b'application', b'pdf', (b'name', b'Love letter.pdf'), b'<17acdbf96cc7f74e7e76>', None, b'base64', 591456, None, 
                    (b'attachment', (b'filename', b'Love letter.pdf')), None, None)
            ], 
            b'mixed', 
            (b'boundary', b'0000000000007dda1105c7b3e2d4'), 
            None, 
            None, 
            None
        ),
    },
    {
        'name': 'text/plain with message/rfc822 attachment',
        'body_structure': 
        (
            [
                (b'text', b'plain', (b'charset', b'utf-8'), None, None, b'7bit', 35, 7, None, None, None, None), 
                (b'message', b'rfc822', (b'name', b'Please respond.eml'), None, None, b'7bit', 10034116, 
                    (
                        b'Sun, 19 Sep 2021 10:04:43 +0200', 
                        b'Please respond', 
                        ((b'Peter Mooring', None, b'petermooring', b'gmail.com'),), 
                        ((b'Peter Mooring', None, b'petermooring', b'gmail.com'),), 
                        ((b'Peter Mooring', None, b'petermooring', b'gmail.com'),), 
                        ((b'peterpm@xs4all.nl', None, b'peterpm', b'xs4all.nl'),), 
                        None, 
                        None, 
                        None, 
                        b'<8b678e28-d03a-2bdd-2930-12470235ef9a@gmail.com>'), 
                    (
                        (b'text', b'plain', (b'charset', b'utf-8'), None, None, b'7bit', 46, 7, None, None, None, None), 
                        (b'text', b'html', (b'charset', b'UTF-8'), None, None, b'quoted-printable', 21375, 397, None, None, None, None),
                        b'alternative', 
                        (b'boundary', b'------------3F95A42110AABF9E24EC86EB'), 
                        None, 
                        (b'en-US',), 
                        None
                    ), 
                    128759, 
                    None, 
                    (b'attachment', (b'filename', b'Please respond.eml')), None, None
                )
            ], 
            b'mixed', 
            (b'boundary', b'------------7323DBBF0E22BDA4B95E42D1'), 
            None, 
            (b'en-US',), 
            None
        ),
    },
    {
        'name': 'text/plain with message/rfc822 attachment including another message/rfc822',
        'body_structure': 
        (
            [
                (b'text', b'plain', (b'charset', b'utf-8'), None, None, b'7bit', 35, 7, None, None, None, None), 
                (b'message', b'rfc822', (b'name', b'Re: My message.eml'), None, None, b'7bit', 10034116, 
                    (
                        b'Sun, 19 Sep 2021 10:04:43 +0200', 
                        b'Re: My message', 
                        ((b'Bob Smith', None, b'bobsmith', b'example.com'),), 
                        ((b'Bob Smith', None, b'bobsmith', b'example.com'),), 
                        ((b'Bob Smith', None, b'bobsmith', b'example.com'),), 
                        ((b'richardroe@example.org', None, b'richardroe', b'example.org'),), 
                        None, 
                        None, 
                        None, 
                        b'<8b678e28-d03a-2bdd-2930-12470235ef9a@example.com>'
                    ), 
                    (
                        (b'text', b'plain', (b'charset', b'utf-8'), None, None, b'7bit', 46, 7, None, None, None, None), 
                        (b'message', b'rfc822', (b'name', b'Fw: Some email two.eml'), None, None, b'7bit', 10029135, 
                            (
                                b'Sat, 18 Sep 2021 18:35:47 +0200', 
                                b'Fw: Some email two', 
                                ((b'John Doe', None, b'johndoe', b'example.org'),), 
                                ((b'John Doe', None, b'johndoe', b'example.org'),), 
                                ((b'John Doe', None, b'johndoe', b'example.org'),), 
                                ((None, None, b'richardroe', b'example.org'),), 
                                None,
                                None,
                                None,
                                b'<a7dfbf41-1a26-4316-b8b5-1753fc17cd54-1631982946791@3c-example.org>'), 
                            (
                                (b'text', b'html', (b'charset', b'UTF-8'), None, None, b'7bit', 5053, 89, None, None, None, None),
                                (b'application', b'pdf', (b'name', b'YZ345.pdf'), None, None, b'base64', 187354, None, 
                                    (b'attachment', (b'filename', b'YZ345.pdf')), None, None),
                                b'mixed', 
                                (b'boundary', b'abmob-49888c7b-3a09-4d10-b119-df9366de9f4c'), 
                                None, 
                                None, 
                                None
                            ), 
                            128656,
                            None,
                            (b'attachment', (b'filename', b'Fw: Some email two.eml')), 
                            None, 
                            None
                        ), 
                        b'mixed', 
                        (b'boundary', b'------------3F95A42110AABF9E24EC86EB'), 
                        None, 
                        (b'en-US',), 
                        None
                    ), 
                    128759, 
                    None, 
                    (b'attachment', (b'filename', b'Fw: Some email two.eml')), None, None
                )
            ],
            b'mixed', 
            (b'boundary', b'------------7323DBBF0E22BDA4B95E42D1'), 
            None, 
            (b'en-US',), 
            None
        ),
    },
]


# show examples
for b in body_structures:
    print('\nBody structure: {}\n{}'.format(b['name'], '-'*60))
    for body_structure_part in IMAPBodyStructureParser.parse(b['body_structure']):
        print('{}'.format(body_structure_part))

Un mot sur IMAP et la vie privée

IMAP a été conçu pour laisser vos messages sur un serveur IMAP , ils sont accessibles depuis plusieurs appareils. Avec IMAP , vous ne demandez initialement qu'un minimum de données. Si vous en voulez plus, seules les parties sélectionnées sont téléchargées. Les demandes de recherche sont également envoyées au serveur IMAP . Personnellement, je n'aime pas IMAP parce qu'il laisse une plus grande partie de vos données sur le serveur IMAP et que l'affichage de pièces jointes spécifiques et les demandes de recherche peuvent également être utilisés pour les empreintes digitales.

Résumé

La mise à plat de la IMAP BODYSTRUCTURE m'a pris du temps car il n'y avait pas de recettes Python sur internet. Après avoir lu la RFC3501 , cela ne semblait pas si difficile... mais... Parce que nous décodons le BODYSTRUCTURE nous-mêmes, il est facile de faire des erreurs. Et pouvons-nous traiter tous les types de BODYSTRUCTURE (malformés) ?
Sur Internet, vous pouvez trouver des informations sur des décodeurs qui échouent parfois, par exemple celui de RoundCube. Ils se replient en récupérant le message entier.

Liens / crédits

IMAP BODYSTRUCTURE: formatted examples
http://sgerwk.altervista.org/imapbodystructure.html

IMAPClient
https://imapclient.readthedocs.io/en/2.2.0/index.html

INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1
https://datatracker.ietf.org/doc/html/rfc3501

En savoir plus...

Email IMAP

Laissez un commentaire

Commentez anonymement ou connectez-vous pour commenter.

Commentaires

Laissez une réponse

Répondez de manière anonyme ou connectez-vous pour répondre.