IMAPClient et mise à plat de la BODYSTRUCTURE
L'aplatissement de la BODYSTRUCTURE nécessite que vous commenciez à lire la RFC3501.
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
Récent
- Masquer les clés primaires de la base de données UUID de votre application web
- Don't Repeat Yourself (DRY) avec Jinja2
- SQLAlchemy, PostgreSQL, nombre maximal de lignes par user
- Afficher les valeurs des filtres dynamiques SQLAlchemy
- Transfert de données sécurisé grâce au cryptage à Public Key et à pyNaCl
- rqlite : une alternative à haute disponibilité et dist distribuée SQLite
Les plus consultés
- Utilisation des Python's pyOpenSSL pour vérifier les certificats SSL téléchargés d'un hôte
- Utiliser UUIDs au lieu de Integer Autoincrement Primary Keys avec SQLAlchemy et MariaDb
- Connexion à un service sur un hôte Docker à partir d'un conteneur Docker
- Utiliser PyInstaller et Cython pour créer un exécutable Python
- SQLAlchemy : Utilisation de Cascade Deletes pour supprimer des objets connexes
- Flask RESTful API validation des paramètres de la requête avec les schémas Marshmallow