Import_mbox.sh не работает с письмами с телефона Samsung, отправленными через сервер listserv

В моей тестовой среде обнаружилась странная проблема: я копирую электронные письма на сервер Discourse и запускаю скрипт import_mbox.sh для их импорта. Исходные письма поступают из рассылки listserv.

Я выяснил, что если пользователи используют телефоны Samsung и отвечают на предыдущее письмо из рассылки, то при попытке импортировать полученное письмо в Discourse новый контент не извлекается. Вместо этого отображается дубликат оригинального письма, но с пометкой, будто его написал отвечающий.

Если я копирую и вставляю сырое проблемное письмо в поле «Emails/Advanced Test», та же проблема сохраняется. Если же я укорачиваю письмо и удаляю несколько добавленных Samsung частей, всё работает.

Я не могу разместить здесь копии писем, вызывающих эту проблему, так как они конфиденциальны. Письма, которые не импортируются, содержат подобные секции (и в них нет текста, читаемого человеком — всё закодировано в base64):

[здесь обрезанные заголовки]
Content-Type: multipart/alternative;
	boundary="--_com.samsung.android.email_341310020171250"

----_com.samsung.android.email_341310020171250
Content-Transfer-Encoding: base64
Content-Type: text/plain; charset=UTF-8

VGhlIGxlZ2lzbGF0aW9uI[обрезано]
[...]
[обрезано]X19fX19fX19fX18NCg==
----_com.samsung.android.email_341310020171250
Content-Type: multipart/related;
	boundary="--_com.samsung.android.email_341310031317791"

Значит, вам нужно изменить import_mbox.sh, чтобы усекать письмо и удалять этот Samsung-бессмыслицу.

Это может быть проблемой, которую можно решить в ядре, так как такие сообщения, вероятно, не проходят обработку при импорте по почте (но я давно не смотрел код, поэтому точно не знаю). В любом случае, самое быстрое решение, скорее всего, — модифицировать скрипт импорта для таких сообщений.

Или, возможно, кто-то узнает эту проблему в ядре и исправит её.

После более тщательного исследования выяснилось, что приложение Samsung Mail кодирует обе части — текстовую и HTML — в формате base64. Я обнаружил, что если добавить пустую строку между этими двумя кодировками, то почтовый фильтр начинает работать корректно. Возможно, Samsung не добавляет пустую строку там, где это необходимо, либо почтовый фильтр некорректно определяет расположение текстовой и HTML-частей и не понимает, что после обнаружения HTML-части он должен знать, где заканчивается заголовок этой части и начинается содержимое сообщения.

Я пробовал скопировать исходное письмо из Gmail (через функцию «Просмотреть исходный код») и также экспортировать то же сообщение из Thunderbird, но результат был тем же.

Письма, сгенерированные Samsung, выглядят следующим образом в нижней части заголовков:

Content-Type: multipart/alternative;
	boundary="--_com.samsung.android.email_396413402758380"

----_com.samsung.android.email_396413402758380
Content-Transfer-Encoding: base64
Content-Type: text/plain; charset=UTF-8

WWVz[здесь следует закодированное в base64 текстовое сообщение]

и заканчивается так:

[здесь больше данных в base64]19fDQo=
----_com.samsung.android.email_396413402758380
Content-Transfer-Encoding: base64
Content-Type: text/html; charset=UTF-8

PGh0b[снова кодировка в base64, на этот раз HTML-версия того же сообщения]

и заканчивается:

[еще данные в base64]NCg==
----_com.samsung.android.email_396413402758380--

Теперь, если я изменю середину, добавив пустую строку (после фрагмента “email_396413402758380”), всё работает идеально!

[здесь больше данных в base64]19fDQo=
----_com.samsung.android.email_396413402758380

Content-Transfer-Encoding: base64
Content-Type: text/html; charset=UTF-8

PGh0b[снова кодировка в base64, на этот раз HTML-версия того же сообщения]

Не говорит ли это о наличии ошибки в импортере?

Мне кажется, это ошибка в почтовом клиенте Samsung. Но я не уверен, и неважно, чья именно это ошибка.

Однако самое простое решение — добавить gsub в скрипт импорта, который добавит пустую строку, о которой вы говорите.

Вероятно, проще вставить строку перед Content-Transfer-Encoding: base64.

Надеюсь, это ничего не сломает.

Но Герхард прямо сейчас пишет более подробный ответ…

Что ж, в таком случае я бы сказал, что это либо ошибка в mail gem, который мы используем для парсинга писем, либо ошибка в приложении Samsung. После беглого взгляда на RFC я склоняюсь к тому, что это ошибка в парсере.

Не могли бы вы предоставить полный пример такого проблемного письма? Может быть, вы сможете попросить одного из авторов ваших конфиденциальных писем отправить вам неконфиденциальное письмо?

Я попытался сымитировать письмо, декодировав base64, изменив формулировки, а затем снова закодировав его, и обнаружил ещё одну интересную особенность.

Удаление пробела посередине исходного сообщения позволяет корректно извлечь ответ, написанный выше.

В этом примере, если в середине base64-кодированного HTML-сообщения найти строку, содержащую [пробел] перед закрывающим тегом /div, и удалить его, то есть изменить

21  20:17  (GMT+00:00) </div><div>To: LIST@LISTS

на

21  20:17  (GMT+00:00)</div><div>To: LIST@LISTS

путём удаления символа [пробел] перед /div, а затем снова закодировать в base64 и вставить обратно в поле для тестирования сообщения в настройках администратора, фильтр сработает.

Могу отправить письмо через прямое сообщение, если это поможет?

Вот придуманное мной письмо, которое, как мне кажется, демонстрирует проблему. Если посмотреть на HTML-часть, там есть ответ на предыдущее сообщение. Импортёр, похоже, не может определить, где начинается исходное сообщение.

From someone@gmail
Delivered-To: someone@gmail
Importance: Normal
MIME-Version: 1.0
Message-ID: <E1mt6gg-00H2OV-6N@relay01.mail.eu.clara.net>
Date: Fri, 3 Dec 2021 11:25:05 +0000
From: someone <someone@somewhere.net>
Subject: Re: Example e-mail
To: <LIST@LISTSERV>
In-Reply-To: <007301d7e834$c268a3e0$4739eba0$@sslmc.co.uk>
Precedence: list
Content-Type: multipart/alternative;
	boundary="--_com.samsung.android.email_7076959834053910"

----_com.samsung.android.email_7076959834053910
Content-Transfer-Encoding: base64
Content-Type: text/plain; charset=UTF-8

WWVzIGFuZCB3ZSBhcmUgZ2V0dGluZyBsb3RzIGluIEFCQyBhbmQgdXJnZW50IGNhcmUsIGFsb25n
IHdpdGggdmFjY2luYXRpb24gc2lkZSBlZmZlY3RzIGZyb20gZmx1ICsgYm9vc3Rlci4gQXBwYXJl
bnRseSBERUYxMTEgY2FuJ3QgZGVhbCB3aXRoIHRoaXMgc29ydCBvZiBxdWVyeS4gTG9va2luZyBn
b29kIGZvciBYbWFzIGFuZCBOWSB3ZWVrICAgIDIyMjIKClNhbQoKRHIgU2FtIFNtaXRoeSAKCgot
LS0tLS0tLSBPcmlnaW5hbCBtZXNzYWdlIC0tLS0tLS0tCkZyb206IFNvdXRoIFNvdXRocyBYWVog
PGVucXVpcnlAU1NYWVouQ08uVUs+CkRhdGU6IDAzLzEyLzIwMjEgMTA6NTkgKEdNVCswMDowMCkK
VG86IExJU1RTQExJU1RTRVJWLkFCQy5PUkcuVUsKU3ViamVjdDogZXZlcnl0aGluZyBsYW5kcyBi
YWNrIGF0IG91ciBkb29yIQoKUHJhY3RpY2VzIHJlcG9ydGluZyB0byBnZXQgIDIgb3IgMyBxdWVy
aWVzL2RheSBmcm9tIHBhdGllbnRzIHJlZ2FyZGluZyBmaXZlcyB2YWNjaW5lIGlzc3VlcyB3aG8g
aGF2ZSBiZWVuIHJlZmVycmVkIHRvIHRoZWlyIEdQIGJ5IFhZWiAxMjMgb3IgdGhlIE5CUy4gIFRo
ZXNlIHF1ZXJpZXMgaW5jbHVkZSBzY2hlZHVsaW5nIGRvc2VzIGZvciBpbW11bm9zdXBwcmVzc2Vk
IHB0cyBhcyB3ZWxsIGFzIHBoYXJtYWNldXRpY2FsIHF1ZXN0aW9ucy4gIA==
----_com.samsung.android.email_7076959834053910
Content-Transfer-Encoding: base64
Content-Type: text/html; charset=UTF-8

PGh0bWw+PGhlYWQ+PG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0
L2h0bWw7IGNoYXJzZXQ9VVRGLTgiPjwvaGVhZD48Ym9keSBkaXI9ImF1dG8iPjxkaXYgZGlyPSJh
dXRvIj5ZZXMgYW5kIHdlIGFyZSBnZXR0aW5nIGxvdHMgaW4gQUJDIGFuZCB1cmdlbnQgY2FyZSwg
YWxvbmcgd2l0aCB2YWNjaW5hdGlvbiBzaWRlIGVmZmVjdHMgZnJvbSBmbHUgKyBib29zdGVyLiBB
cHBhcmVudGx5IERFRjExMSBjYW4ndCBkZWFsIHdpdGggdGhpcyBzb3J0IG9mIHF1ZXJ5LiBMb29r
aW5nIGdvb2QgZm9yIFhtYXMgYW5kIE5ZIHdlZWsmbmJzcDsgJm5ic3A7IDIyMjI8L2Rpdj48ZGl2
IGRpcj0iYXV0byI+PGJyPjwvZGl2PjxkaXYgZGlyPSJhdXRvIj5TYW08L2Rpdj48ZGl2IGRpcj0i
YXV0byI+PGJyPjwvZGl2PjxkaXYgaWQ9ImNvbXBvc2VyX3NpZ25hdHVyZSIgZGlyPSJhdXRvIj48
bWV0YSBodHRwLWVxdWl2PSJDb250ZW50LVR5cGUiIGNvbnRlbnQ9InRleHQvaHRtbDsgY2hhcnNl
dD1VVEYtOCI+RHIgU2FtIFNtaXRoeSZuYnNwOzwvZGl2PjxkaXYgZGlyPSJhdXRvIj48YnI+PC9k
aXY+PGRpdj48YnI+PC9kaXY+PGRpdiBhbGlnbj0ibGVmdCIgZGlyPSJhdXRvIiBzdHlsZT0iZm9u
dC1zaXplOjEwMCU7Y29sb3I6IzAwMDAwMCI+PGRpdj4tLS0tLS0tLSBPcmlnaW5hbCBtZXNzYWdl
IC0tLS0tLS0tPC9kaXY+PGRpdj5Gcm9tOiBTb3V0aCBTb3V0aHMgWFlaICZsdDtlbnF1aXJ5QFNT
WFlaLkNPLlVLJmd0OyA8L2Rpdj48ZGl2PkRhdGU6IDAzLzEyLzIwMjEgIDEwOjU5ICAoR01UKzAw
OjAwKSA8L2Rpdj48ZGl2PlRvOiBMSVNUU0BMSVNUU0VSVi5BQkMuT1JHLlVLIDwvZGl2PjxkaXY+
U3ViamVjdDogZXZlcnl0aGluZyBsYW5kcyBiYWNrIGF0IG91ciBkb29yISA8L2Rpdj48ZGl2Pjxi
cj48L2Rpdj48L2Rpdj48ZGl2IGNsYXNzPSJXb3JkU2VjdGlvbjEiPjxwIGNsYXNzPSJNc29Ob3Jt
YWwiPjxzcGFuIHN0eWxlPSJmb250LWZhbWlseTomcXVvdDtBcmlhbCZxdW90OywmcXVvdDtzYW5z
LXNlcmlmJnF1b3Q7Ij5QcmFjdGljZXMgcmVwb3J0aW5nIHRvIGdldCAmbmJzcDsyIG9yIDMgcXVl
cmllcy9kYXkgZnJvbSBwYXRpZW50cyByZWdhcmRpbmcgZml2ZXMgdmFjY2luZSBpc3N1ZXMgd2hv
IGhhdmUgYmVlbiByZWZlcnJlZCB0byB0aGVpciBHUCBieSBYWVogMTIzIG9yIHRoZSBOQlMuJm5i
c3A7IFRoZXNlIHF1ZXJpZXMgaW5jbHVkZSBzY2hlZHVsaW5nIGRvc2VzIGZvciBpbW11bm9zdXBw
cmVzc2VkIHB0cyBhcyB3ZWxsIGFzIHBoYXJtYWNldXRpY2FsIHF1ZXN0aW9ucy4mbmJzcDsgPC9z
cGFuPjwvcD48L2Rpdj48L2JvZHk+PC9odG1sPg==
----_com.samsung.android.email_7076959834053910--

Похоже, эта проблема также затрагивает сообщения из других почтовых клиентов, что я сейчас и обнаружил. Я не могу опубликовать в открытом доступе письма, вызывающие сбои, чтобы все могли их посмотреть, но с радостью покажу их кому-либо в частном порядке.

Моя текущая конфигурация следующая: я установил Discourse на домашний сервер. Письма, отправленные на рассылку listserv, приходят ко мне (в аккаунт Gmail). Если фильтр «Кому:» совпадает с именем рассылки, Gmail пересылает копию письма на адрес mailinglist@mydiscoursedomain.org.uk. В Discourse настроена категория для зеркального отображения рассылки, которая ищет эти письма.

Та же проблема возникает и при использовании скрипта import_mbox.sh после ручного копирования писем, следовательно, проблема кроется в той части кода, которая ищет новую часть сообщения и которая запутывается.

Есть ли способ заставить Discourse быстро обработать все ранее импортированные электронные письма и попытаться переформатировать их, используя текстовую часть оригинальных писем, если это может стать временным решением описанной выше проблемы? До импорта была включена опция использования HTML-части. Изучая данные через ‘rails c’, я вижу, что у каждого поста, похоже, хранится полный текст входящих сообщений (включая заголовки электронной почты). Я пробовал запускать ‘rake posts:rebuild’ после отключения опции HTML, и хотя процесс медленно проходит по всем сообщениям, я не уверен, что что-то изменилось. Например, я пробовал включать и выключать опцию отображения обрезанного содержимого, но маленькое поле с тремя точками всё ещё появляется в постах после завершения команды rake.