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

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.

Теги

 

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