Перейти к основному содержанию

hMailServer — вырезаем текст из письма 2

hmailserver

В прошлом году написал статью, как вырезать текст из письма:

hMailServer — вырезаем текст из письма

Иногда требуется вырезать кусок лишнего текста, это может быть:

  • ненужный рекламный блок
  • нежелательный футер
  • чувствительная информация в виде номеров банковских карт или баланс счёта
  • любая другая ненужная информация, от которой нужно избавиться

Всё было прекрасно до определённого момента, пока отправитель не изменил HTML тело письма. В письмо была вставлена какая-то кривая картинка, которую COM API сервера не смог распознать. Всё как здесь:

https://github.com/hmailserver/hmailserver/issues/28

Разработчик hMailServer исправил ошибку, но она выстрелила снова. Симптомы следующие:

Если мы просто получаем письмо, или переадресуем его, то всё проходит успешно. Но стоит вызвать собственную функцию и попытаться получить oMessage.HTMLBody, то письмо считывается не полностью, код обрывается на середине. Мы просто теряем возможность работать с HTML телом письма. Если бы в письме сообщение дублировалось через PLAIN TEXT, то можно было бы работать с ним, однако, у нас чистый HTML.

Если мы вызываем такую функцию, то проблем нет:

Sub MailFabric(oMessage)
	if oMessage.HasBodyType("text/html") then
		dim x
		'что-то своё делаем, не трогая письмо
	end if
	oMessage.Save
End Sub

Как только мы получаем oMessage.HTMLBody и перезаписываем, то всё плохо:

Sub MailFabric(oMessage)
	if oMessage.HasBodyType("text/html") then
		oMessage.HTMLBody = oMessage.HTMLBody
	end if
	oMessage.Save
End Sub

Бубен не помог, будем добавлять танцы.

Скрипт для вырезания текста из сообщения

Раз наше COM API не работает в плане получения текста HTML, то будем писать своё. В этом нам поможет переменная oMessage.Filename, в которой содержится путь к EML файлу письма. Зная путь, мы можем сами открыть файл, распарсить его и получить вожделенный HTML. Исправим его и засунем обратно в письмо. Приступим.

Включаем скрипты: Настройки → Дополнительно → Скрипты.

mail

Сохранить.

Кнопка Показать откроет директорию с файлом скриптов. По умолчанию это C:\Program Files (x86)\hMailServer\Events\EventHandlers.vbs. Редактируем файл EventHandlers.vbs. Следует помнить, что после внесения изменений в файл нужно нажать кнопку Перезагрузить, при этом изменения внесутся в память hMailServer. Кнопка Проверка проверяет код на наличие ошибок.

Добавляем код:

  • Sub MailClean(oMessage)
    	dim FSO, OFile
    	dim MyPattern, objRegExp
    	
    	'если письмо HTML, то танцы с бубном
    	if oMessage.HasBodyType("text/html") then
    		oMessage.RefreshContent()
    		set FSO = CreateObject("Scripting.FileSystemObject")
    		
    		'из-за ошибки в COM API мы не можем читать кривой код oMessage.HTMLbody,
    		'поэтому сами получим его из файла письма.
    		dim oMHTMLBody
    		
    		'вытаскиваем тело письма из файла
    		oMHTMLBody = TakeHTMLFromEML(oMessage.Filename)
    		
    		'декодируем из Base64
    		oMHTMLBody = Base64Decode(oMHTMLBody)
    		
    		'вырезаем строку
    		
    		'регулярное выражение для вырезания строки из тела письма
    		MyPattern = "Ваш(<.*>)?[\s]*(<.*>)?баланс(<.*>)?[\s]*(<.*>)?[0-9]\d*(,|\.)[0-9]{1,2}(<.*>)?\."
    
    		'мы пишем здесь на UTF-8, а hMailServer работает с ISO-8859-1, переводим нашу регулярку в другую кодировку
    		MyPattern = StrConv(MyPattern,"UTF-8","ISO-8859-1")
    
    		'объект для регулярного выражения [Global = True] ищет все вхождения, если нужно вырезать только первое, сто ставим false
    		Set objRegExp = CreateObject("VBScript.RegExp")
    		objRegExp.Pattern = MyPattern
    		objRegExp.Global = True
    		objRegExp.IgnoreCase = False
    		objRegExp.Multiline = True
    		oMessage.HTMLBody = objRegExp.Replace(oMHTMLBody, "")
    	end if
    	
    	'если письмо текстовое, то правим oMessage.Body
    	if oMessage.HasBodyType("text/plain") then
    		'вырезаем строку
    		
    		'регулярное выражение для вырезания строки из тела письма
    		MyPattern = "Ваш баланс [0-9]\d*,[0-9]{1,2}\."
    		
    		'объект для регулярного выражения [Global = True] ищет все вхождения, если нужно вырезать только первое, сто ставим false
    		Set objRegExp = CreateObject("VBScript.RegExp")
    		objRegExp.Pattern = MyPattern
    		objRegExp.Global = True
    		objRegExp.IgnoreCase = False
    		objRegExp.Multiline = False
    		oMessage.Body = objRegExp.Replace(oMessage.Body, "")
    	end if
    	
    	oMessage.Save
    End Sub
    
    Function StrConv(Text,SourceCharset,DestCharset)
    	Set Stream = CreateObject("ADODB.Stream")
    	Stream.Type=2
    	Stream.Mode=3
    	Stream.Open
    	Stream.Charset=DestCharset
    	Stream.WriteText Text
    	Stream.Position=0
    	Stream.Charset=SourceCharset
    	StrConv = Stream.ReadText
    End Function
    
    Function TakeHTMLFromEML(PathEML)
    	TakeHTMLFromEML = ""
    
    	dim FSO
    	set FSO = CreateObject("Scripting.FileSystemObject")
    			
    	'читаем файл письма сами (1 = ForReading)
    	dim fM, fMcontent
    	Set fM = FSO.OpenTextFile(PathEML, 1)
    	fMcontent = fM.ReadAll
    	fM.Close
    
    	'выбираем контент письма
    	dim fMpattern, fMRE, fMatches, fMHTMLcontent
    	fMpattern = "--boundary[\s\S]*--boundary--"
    	Set fMRE = CreateObject("VBScript.RegExp")
    	fMRE.Pattern = fMpattern
    	fMRE.Global = False
    	fMRE.IgnoreCase = False
    	fMRE.Multiline = True
    	Set fMatches = fMRE.Execute(fMcontent)
    	if fMatches.Count > 0 Then
    		'вырезаем лишнее
    		fMHTMLcontent = fMatches.Item(0).Value
    		fMHTMLcontent = Replace(fMHTMLcontent, "--boundary--", "")
    		fMHTMLcontent = Replace(fMHTMLcontent, "--boundary", "")
    		fMHTMLcontent = Replace(fMHTMLcontent, "Content-Type: text/html; charset=utf-8", "")
    		fMHTMLcontent = Replace(fMHTMLcontent, "Content-Transfer-Encoding: base64", "")
    		fMHTMLcontent = Trim(fMHTMLcontent)
    		TakeHTMLFromEML = fMHTMLcontent
    	end if
    End Function
    
    Function Base64Decode(ByVal vCode)
    	Dim oXML, oNode
    	Set oXML = CreateObject("Msxml2.DOMDocument.3.0")
    	Set oNode = oXML.CreateElement("base64")
    	oNode.dataType = "bin.base64"
    	oNode.text = vCode
    	Base64Decode = Stream_BinaryToString(oNode.nodeTypedValue)
    	Set oNode = Nothing
    	Set oXML = Nothing
    End Function
    
    'Stream_BinaryToString Function
    '2003 Antonin Foller, http://www.motobit.com
    'Binary - VT_UI1 | VT_ARRAY data To convert To a string 
    Function Stream_BinaryToString(Binary)
    	Const adTypeText = 2
    	Const adTypeBinary = 1
    
    	'Create Stream object
    	Dim BinaryStream 'As New Stream
    	Set BinaryStream = CreateObject("ADODB.Stream")
    
    	'Specify stream type - we want To save binary data.
    	BinaryStream.Type = adTypeBinary
    
    	'Open the stream And write binary data To the object
    	BinaryStream.Open
    	BinaryStream.Write Binary
    
    	'Change stream type To text/string
    	BinaryStream.Position = 0
    	BinaryStream.Type = adTypeText
    
    	'Specify charset For the output text (unicode) data.
    	'BinaryStream.CharSet = "us-ascii"
    	BinaryStream.CharSet = "UTF-8"
    
    	'Open the stream And get text/string data from the object
    	Stream_BinaryToString = BinaryStream.ReadText
    	Set BinaryStream = Nothing
    End Function

Здесь:

  • MailClean — функция удаления ненужного текста из тела письма
  • oMessage.RefreshContent() — не обязательная вещь, перечитывает содержимое письма в COM API объект.
  • TakeHTMLFromEML — функция для вытаскивания из EML тела письма, часть данных я стираю
  • Base64Decode — тело письма оказалось закодировано в Base64, декодер
  • Stream_BinaryToString — вспомогательная функция для декодирования, обратите внимание на кодировку внутри
  • StrConvert — вспомогательная функция для конвертации текста, нужна для поддержки кириллицы
  • MyPattern — регулярное выражение для поиска ненужной строки, разное, в зависимости от типа письма

Стоит обратить внимание на работу с кодировками, особенно если требуется работать с кириллицей. И не забывайте о том, что письмо может быть текстовым или HTML. TakeHTMLFromEML — здесь можно было бы использовать регулярное выражение для поиска Base64, но я пошёл по более простому пути, мне нужно было изменить только один шаблон письма от одного отправителя.

Пример настройки ящика

Настроим ящик cleantest@internet-lab.ru, Переключаемся на вкладку правила.

hmail

Добавляем новое правило, у меня уже добавлено, назвал его: "CleanTest".

hmail

Какой добавлять критерий — решать вам. Если нужно обрабатывать все сообщения, указываем Размер сообщения > 0. Если нужно чистить письма только от определённого отправителя или получателя — указываем соответствующий адрес. Критериев может быть несколько.

Добавляем действие "Выполнить скрипт".

mail

Выполняем скрипт MailClean. Сохраняем изменения. P.S. VBS скрипт у меня сохранён в UTF-8. Без BOM.

Конец

Данный пример — не панацея от всех бед. Но на конкретном примере мы показали, что некоторые недоработки COM API в hMailServer мы можем обойти, использую свои собственные руки. Очень жаль, что разработчик hMailServer почти не занимается разработкой. На форуме есть предложение взять управление эти бесплатным сервером в свои руки. В любом случае, код доступен на GitHub.

Теги

 

Похожие материалы

Тестовый вирус EICAR

Для проверки антивирусов существует специальный тестовый командный файл eicar.com. Файл не содержит фрагментов вирусного кода, поэтому его безопасно пересылать. Большинство антивирусов реагируют на него как на вирус, обычно указывая соответствующее название, к примеру EICAR-AV-Test.

Теги