IMAPClient и уплощение BODYSTRUCTURE
Уплощение BODYSTRUCTURE требует, чтобы вы начали читать RFC3501.
Разработчики приложений хотят использовать проверенные решения для создания приложения. Во многих случаях это работает, но в пакете IMAPClient не хватает нескольких вещей.
Вся идея IMAP заключается в том, чтобы получить только то, что вы запрашиваете. Предположим, у вас есть письмо с большим количеством вложений, но вы хотите просмотреть или скачать только одно из них. Для этого вам нужен 'body_number' этого вложения, а затем FETCH этой части.
В интернете можно увидеть людей, которые скачивают все сообщение целиком, но это не тот способ, которым можно это сделать! Здесь я представляю решение для получения body_numbers всех частей сообщения электронной почты.
Сглаживание BODYSTRUCTURE
Я сам боролся с этим, и для начала я использовал некоторый код, который нашел в интернете, но пользы от него было мало. Пора начать читать RFC3501, смотрите ссылки ниже. Сообщение электронной почты IMAP состоит не только из вложений, таких как файлы images и PDF , но и сами вложения могут быть сообщениями, что означает, что наш код должен использовать рекурсию.
Мне нужен список body_numbers , который можно использовать для FETCH нужной нам части (частей). Мы можем назвать эту операцию "сглаживание BODYSTRUCTURE", потому что мы переходим от рекурсии к списку. В ходе этого процесса мы создаем body_numbers.
MULTIPART/ALTERNATIVE, MULTIPART/MIXED, MULTIPART/RELATED, ...
Сообщения электронной почты чаще всего состоят из элементов, связанных друг с другом. Существуют различные типы отношений, например:
- ALTERNATIVE: альтернативные части имеют одинаковое содержание, так что почтовый клиент может выбрать, какую из них показать.
- RELATED: части должны быть представлены вместе, а не альтернативно. В результате они объединяются. например, встроенное изображение
- MIXED: части содержат разную информацию и не должны быть показаны вместе.
Документ 'IMAP BODYSTRUCTURE: formatted examples' дает хорошее введение, см. ссылки ниже.
Вот пример отношения ALTERNATIVE . BODYSTRUCTURE , возвращаемый IMAPClient , является:
(
[
(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 дает нам список элементов ALTERNATIVE . Мы можем выбрать показать либо обычный текст, либо часть HTML . Сплющенный body_parts - это, первое число - body_number:
1 - ALTERNATIVE : TEXT, text/plain, iso-8859-1
2 - ALTERNATIVE : TEXT, text/html, iso-8859-1
Вот тот же пример с добавлением вложения PDF :
(
[
(
[
(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
)
Сплющенные body_parts , первый номер - 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
Откуда берутся body_numbers ?
Мы не получаем body_numbers с сервера IMAP . Вместо этого мы получаем BODYSTRUCTURE с сервера IMAP и должны сгенерировать из него body_numbers . В приведенных выше примерах это несложно. Но с вложенными сообщениями все становится сложнее.
Структура BODYSTRUCTURE
Чтобы понять структуру BODYSTRUCTURE , я скопировал часть текста из RFC3501:
Основные поля многостороннего body part расположены в следующем порядке:
- body type
Строка с именем типа носителя содержимого, как определено в [MIME-IMB]. -
body subtype
Строка с именем подтипа содержимого, как определено в [MIME-IMB]. -
body parameter parenthesized list
Список пар атрибутов/значений в круглых скобках [например, ("foo" "bar" "baz" "rag"), где "bar" - значение "foo", а "rag" - значение "baz"], как определено в [MIME-IMB]. -
body id
Строка, дающая идентификатор содержимого, как определено в [MIME-IMB]. -
body description
Строка, указывающая содержимое , как определено в [MIME-IMB]. -
body encoding
Строка с кодировкой передачи содержимого, как определено в [MIME-IMB]. -
body size
Число, указывающее размер тела в октетах. Обратите внимание, что этот размер - размер в кодировке передачи, а не результирующий размер после декодирования.
body type типа MESSAGE и подтипа RFC822 содержит, сразу после основных полей, структуру конверта, структуру тела и размер в текстовых строках инкапсулированного сообщения.
body type типа TEXT содержит, сразу после основных полей, размер тела в текстовых строках. Обратите внимание, что этот размер является размером в кодировке передачи содержимого, а не результирующим размером после декодирования.
Данные расширения следуют за основными полями и полями, специфичными для типа, перечисленными выше. Данные расширения никогда не возвращаются при выборке BODY, но могут быть возвращены при выборке BODYSTRUCTURE . Данные расширения, если они присутствуют, ДОЛЖНЫ быть в определенном порядке.
Обратите внимание, что body type типа MESSAGE и подтипа RFC822 вводит рекурсию. Когда мы сталкиваемся с этим, мы обрабатываем это сообщение как новое сообщение, извлекаем body_number и т.д. И это сообщение снова может содержать одно или несколько других сообщений.
Согласно RFC3501, каждое сообщение содержит структуру конверта и структуру тела. Структура тела сообщения - это новая структура тела, которую мы должны обработать.
Пример вложенных сообщений
Вот пример сообщения, которое содержит другое сообщение, которое содержит другое сообщение с вложением. BODYSTRUCTURE , возвращаемый IMAPClient , является:
(
[
(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
)
Сплющенные body_parts являются, первый номер - 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
Исправление BODYSTRUCTURE вложенных сообщений
Когда вы посмотрите на пример вложенных сообщений выше, вы заметите, что вложенные сообщения не содержат списков, как сообщение верхнего уровня. Я считаю это ошибкой, но, возможно, это дизайнерское решение.
Как бы то ни было, IMAPClient содержит класс, который мы можем использовать для преобразования вложенного сообщения BODYSTRUCTURE в сообщение верхнего уровня BODYSTRUCTURE.
body_data = BodyData()
body_structure = body_data.create(nested_message_body_structure_part)
Теперь вложенное сообщение BODYSTRUCTURE содержит списки и может обрабатываться так же, как и сообщение верхнего уровня BODYSTRUCTURE.
Класс BodyStructurePart и типы частей
Сплющивание означает, что мы создаем список объектов BodyStructurePart. Класс BodyStructurePart содержит всю информацию для дальнейшей обработки. Он должен содержать, по крайней мере, следующее:
- body_number: body_number
- тип_тела: ALTERNATIVE, MIXED и т.д.
- body_part: фактическая часть BODYSTRUCTURE.
Кроме того, я добавил атрибуты:
- part_type
- part_subtype
- content_type
part_type может быть, см. также RFC3501 выше:
- MESSAGE_RFC822
- TEXT
- NON_MULTIPART
part_subtype, используемый с part_type = NON_MULTIPART, может быть:
- ATTACHMENT
- INLINE
- OTHER
Код парсера BODYSTRUCTURE
И, наконец, вот код парсера. Он содержит три класса:
- IMAPBodyStructurePartUtils
- IMAPBodyStructurePart
- IMAPBodyStructureParser
Класс IMAPBodyStructureParser имеет метод parse, который вызывается с помощью BODYSTRUCTURE , возвращаемого IMAPClient. Этот метод возвращает список объектов IMAPBodyStructurePart , которые можно использовать в нашем коде.
Я поместил код и примеры в один файл на случай, если вы захотите попробовать:
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))
Несколько слов о IMAP и конфиденциальности
IMAP был разработан для того, чтобы оставлять ваши сообщения на сервере IMAP , они могут быть доступны с нескольких устройств. С IMAP вы изначально запрашиваете только минимальные данные. Если вам нужно больше, загружаются только выбранные части. Поисковые запросы также отправляются на сервер IMAP . Лично мне не нравится IMAP , потому что он оставляет гораздо больше ваших данных на сервере IMAP , а просмотр конкретных вложений и поисковые запросы могут также использоваться для снятия отпечатков пальцев.
Резюме
Расплющивание IMAP BODYSTRUCTURE заняло у меня много времени, потому что в интернете не было рецептов Python . После прочтения RFC3501 оказалось, что это не так уж и сложно... но... Поскольку мы сами расшифровываем BODYSTRUCTURE , легко допустить ошибки. А сможем ли мы справиться со всеми типами (malformed) BODYSTRUCTURE?
В интернете можно найти информацию о иногда отказывающих декодерах, например, из RoundCube. Они отступают, получая сообщение целиком.
Ссылки / кредиты
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
Недавний
- Скрытие первичных ключей базы данных UUID вашего веб-приложения
- Don't Repeat Yourself (DRY) с Jinja2
- SQLAlchemy, PostgreSQL, максимальное количество строк для user
- Показать значения в динамических фильтрах SQLAlchemy
- Безопасная передача данных с помощью шифрования Public Key и pyNaCl
- rqlite: альтернатива dist с высокой степенью готовности и SQLite
Большинство просмотренных
- Используя Python pyOpenSSL для проверки SSL-сертификатов, загруженных с хоста
- Использование UUID вместо Integer Autoincrement Primary Keys с SQLAlchemy и MariaDb
- Подключение к службе на хосте Docker из контейнера Docker
- Использование PyInstaller и Cython для создания исполняемого файла Python
- SQLAlchemy: Использование Cascade Deletes для удаления связанных объектов
- Flask Удовлетворительный запрос API проверка параметров запроса с помощью схем Маршмэллоу