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

IMAPClient en het afvlakken van de BODYSTRUCTURE

Het afvlakken van de BODYSTRUCTURE vereist dat je begint met het lezen van de RFC3501.

27 september 2021 Bijgewerkt 27 september 2021
In Email
post main image
https://unsplash.com/@2hmedia

Applicatie-ontwikkelaars willen bewezen oplossingen gebruiken om een applicatie te maken. Vaak werkt dit, maar met het IMAPClient pakket ontbreken er een aantal zaken.

Het hele idee van IMAP is om alleen te krijgen wat je vraagt. Stel dat je een e-mail hebt met veel bijlagen, maar je wilt er maar één van bekijken of downloaden. Om dit te kunnen doen heb je de 'body_number' van deze bijlage nodig en vervolgens FETCH dit deel.

Op het internet zie je mensen die het hele bericht downloaden, maar dat is niet de manier om dit te doen! Hier presenteer ik een oplossing om de body_numbers van alle delen van een e-mail bericht te krijgen.

Het afvlakken van de BODYSTRUCTURE

Ik heb hier zelf mee geworsteld en om op gang te komen heb ik wat code gebruikt die ik op het internet had gevonden, maar dat was van beperkt nut. Tijd om de RFC3501 te gaan lezen, zie onderstaande links. Een IMAP e-mail bericht bestaat niet alleen uit bijlagen zoals images en PDF bestanden, maar de bijlagen kunnen zelf ook berichten zijn, wat betekent dat onze code recursie moet gebruiken.

Wat ik wil is een lijst van body_numbers die gebruikt kan worden om FETCH het deel (de delen) dat we willen hebben. We kunnen deze bewerking 'afvlakken van de BODYSTRUCTURE' noemen, omdat we van recursies naar een lijst gaan. Tijdens dit proces genereren we de body_numbers.

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

E-mailberichten zijn meestal opgebouwd uit items die aan elkaar gerelateerd zijn. Er zijn verschillende soorten relaties, bijvoorbeeld:

  • ALTERNATIVE: de onderdelen in alternatief hebben dezelfde inhoud, zodat de mail client kan kiezen welke getoond moet worden
  • RELATED: de onderdelen moeten samen worden gepresenteerd, niet alternerend. Bijgevolg worden ze gecombineerd. bv. inline afbeelding
  • MIXED: de delen bevatten verschillende informatie en dienen niet samen te worden getoond.

Het document "IMAP BODYSTRUCTURE: formatted examples" geeft een aardige inleiding, zie de links hieronder.

Hier is een voorbeeld van een ALTERNATIVE relatie. De BODYSTRUCTURE die door IMAPClient wordt teruggegeven is:

(
    [
        (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 geeft ons een lijst van ALTERNATIVE items. We kunnen kiezen om ofwel de platte tekst of het HTML gedeelte te tonen. De afgeplatte body_parts zijn, eerste nummer is de body_number:

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

Hier is hetzelfde voorbeeld met een PDF hulpstuk toegevoegd:

(
    [
        (
            [
                (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
)

De afgevlakte body_parts zijn, eerste nummer is de 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

Waar komen de body_numbers vandaan?

Wij krijgen de body_numbers niet van de IMAP server. In plaats daarvan krijgen we de BODYSTRUCTURE van de IMAP server en moeten we daaruit de body_numbers genereren. In de bovenstaande voorbeelden is dit niet moeilijk. Maar het wordt ingewikkelder met bijgevoegde berichten.

De structuur van de BODYSTRUCTURE

Om de BODYSTRUCTURE te begrijpen heb ik wat tekst gekopieerd uit RFC3501:

De basisvelden van een niet-multipart body part staan in de onderstaande volgorde:

  • body type
    Een tekenreeks met de naam van het inhoudstype media zoals gedefinieerd in [MIME-IMB].
  • body subtype
    Een tekenreeks die de naam geeft van het inhoudssubtype zoals gedefinieerd in [MIME-IMB].

  • body parameter parenthesized list
    Een tussen haakjes geplaatste lijst van attributen/waardeparen [bv. ("foo" "bar" "baz" "rag") waarbij "bar" de waarde is van "foo" en "rag" de waarde is van "baz"] zoals gedefinieerd in [MIME-IMB].

  • body id
    Een tekenreeks die de inhoud-id geeft zoals gedefinieerd in [MIME-IMB].

  • body description
    Een tekenreeks die de inhoud geeft zoals gedefinieerd in [MIME-IMB].

  • body encoding
    Een tekenreeks die de codering voor inhoudsoverdracht geeft, zoals gedefinieerd in [MIME-IMB].

  • body size
    Een getal dat de grootte van de body in bytes aangeeft. Merk op dat deze grootte de grootte is in de overdrachtscodering en niet de resulterende grootte na decodering.

Een body type van het type MESSAGE en subtype RFC822 bevat onmiddellijk na de basisvelden de structuur van de enveloppe, de structuur van de body en de omvang in tekstregels van het ingekapselde bericht.

Een body type van het type TEXT bevat, onmiddellijk na de basisvelden, de omvang van de body in tekstregels. Merk op dat deze grootte de grootte is in de codering van de inhoudsoverdracht en niet de resulterende grootte na eventuele decodering.

Uitbreidingsgegevens volgen op de basisvelden en de boven opgesomde typespecifieke velden. Uitbreidingsgegevens worden nooit teruggezonden met de BODY-afleiding, maar kunnen worden teruggezonden met een BODYSTRUCTURE -afleiding. Indien er uitbreidingsgegevens aanwezig zijn, MOETEN deze in de gedefinieerde volgorde staan.

Merk op dat de body type van het type MESSAGE en subtype RFC822 recursie introduceert. Wanneer we dit tegenkomen, verwerken we dit bericht als een nieuw bericht, extraheren body_number's, enz. En dit bericht kan weer een of meer andere berichten bevatten.

Volgens de RFC3501, bevat elk bericht een enveloppe structuur, en een body structuur. De body-structuur van het bericht is de nieuwe body-structuur die wij moeten verwerken.

Geneste berichten voorbeeld

Hier is een voorbeeld van een bericht dat een ander bericht bevat dat een ander bericht met een bijlage bevat. De BODYSTRUCTURE die door IMAPClient wordt geretourneerd is:

(
    [
        (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
)

De afgevlakte body_parts zijn, eerste nummer is de 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

De BODYSTRUCTURE van geneste berichten vastleggen

Als je naar het voorbeeld van geneste berichten hierboven kijkt, zul je zien dat geneste berichten geen lijsten bevatten, zoals het bericht op het hoogste niveau. Ik beschouw dit als een bug, maar het kan ook een ontwerpbeslissing zijn.

Hoe dan ook, IMAPClient bevat een klasse die we kunnen gebruiken om het geneste bericht BODYSTRUCTURE te converteren naar een bericht van het hoogste niveau BODYSTRUCTURE.

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

Nu bevat het geneste bericht BODYSTRUCTURE lijsten en kan het op dezelfde manier verwerkt worden als het top niveau BODYSTRUCTURE.

De klasse BodyStructurePart en deel-typen

Flatten betekent dat we een lijst van BodyStructurePart objecten maken. Een BodyStructurePart klasse bevat alle informatie om verdere verwerking te doen. Zij moet tenminste het volgende bevatten:

  • body_number: de body_number
  • body_type: ALTERNATIVE, MIXED, enz.
  • body_part: het eigenlijke deel van de BODYSTRUCTURE

Daarnaast heb ik attributen toegevoegd:

  • part_type
  • part_subtype
  • content_type

Een part_type kan zijn, zie ook RFC3501 hierboven:

  • MESSAGE_RFC822
  • TEXT
  • NON_MULTIPART

Een part_subtype, gebruikt met part_type = NON_MULTIPART, kan zijn:

  • ATTACHMENT
  • INLINE
  • OTHER

De BODYSTRUCTURE parser code

En tenslotte is hier de parser code. Het bevat drie klassen:

  • IMAPBodyStructurePartUtils
  • IMAPBodyStructurePart
  • IMAPBodyStructureParser

De klasse IMAPBodyStructureParser heeft een parse-methode die wordt aangeroepen met de BODYSTRUCTURE die door IMAPClient wordt teruggegeven. Deze methode geeft een lijst van IMAPBodyStructurePart objecten terug die in onze code kunnen worden gebruikt.

Ik heb de code en voorbeelden in een enkel bestand gezet voor het geval je het wilt proberen:

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))

Een woord over IMAP en privacy

IMAP is ontworpen om je berichten op een IMAP server te laten staan, ze kunnen vanaf meerdere apparaten benaderd worden. Met IMAP vraagt u in eerste instantie slechts minimale gegevens op. Als u meer wilt, worden alleen de geselecteerde delen gedownload. Zoekverzoeken worden ook naar de IMAP server gestuurd. Persoonlijk houd ik niet van IMAP omdat het veel meer van je gegevens op de IMAP server laat staan en het bekijken van specifieke bijlagen en zoek verzoeken ook gebruikt kan worden voor fingerprinting.

Samenvatting

Het plat maken van de IMAP BODYSTRUCTURE kostte me tijd omdat er geen Python recepten op het internet te vinden waren. Na het lezen van de RFC3501 bleek het niet zo moeilijk ... maar ... Omdat we de BODYSTRUCTURE zelf ontcijferen kunnen we gemakkelijk fouten maken. En kunnen we alle soorten (misvormde) BODYSTRUCTUREs aan?
Op het internet is informatie te vinden over soms falende decoders, b.v. van RoundCube. Zij vallen terug door het hele bericht op te halen.

Links / credits

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

Lees meer

Email IMAP

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.