• Познакомьтесь с пентестом веб-приложений на практике в нашем новом бесплатном курсе

    «Анализ защищенности веб-приложений»

    🔥 Записаться бесплатно!

  • CTF с учебными материалами Codeby Games

    Обучение кибербезопасности в игровой форме. Более 200 заданий по Active Directory, OSINT, PWN, Веб, Стеганографии, Реверс-инжинирингу, Форензике и Криптографии. Школа CTF с бесплатными курсами по всем категориям.

Разделение RT на 2 отдельных: текст и аттачи

  • Автор темы Omh
  • Дата начала

VladSh

начинающий
Lotus Team
11.12.2009
1 783
157
BIT
53
NickProstoNick
Копируете RT-item, содержащий вложения, во временный документ.
Удаляете оттуда ненужные вложения.
Копируете RT-item с оставшимися вложениями в нужный документ. Ну или делаете append... к существующему item'у нужного документа.
Это самый корректный способ.

Добавлено:
Можно попроще, без сохранения на диск.
Геморройность этого кода уже обсуждали. Как по моему, это не годный код.
 

NickProstoNick

Статус как статус :)
Lotus Team
22.08.2008
1 851
27
BIT
0
VladSh
Да вот мне кажется не все так просто.
Нужно удалить текст, вложенные изображения, таблицы... :)
 

VladSh

начинающий
Lotus Team
11.12.2009
1 783
157
BIT
53
Нужно удалить текст, вложенные изображения, таблицы... :)
Ну тут надо определиться: или в dxl заморачиваемся или удаляем.

Удаление через NotesRichTextNavigator - удалять всё, кроме RTELEM_TYPE_FILEATTACHMENT. Только надо проверить, будут ли удаляться картинки, вставленные из буфера обмена (у класса нет метода, получающего их). Если не покатит, то dxl.
 
T

turumbay

Да вот мне кажется не все так просто.
Нужно удалить текст, вложенные изображения, таблицы... :)
По сабжу пришли к выводу, что задача не решается без dxl - в общем случае аттачи могут быть внутри секций, ячеек таблицы и т.п. Однако и dxl все не так просто как хотелось бы... Вариант решения я тогда отправил автору в личку, постеснявшись выкладывать портянку в паблик :)
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">"Разложить RT поле на два: в одном только аттачи, в другом - все остальное(без потери разметки)"</div></div><div class="sp-body"><div class="sp-content">
Основная проблема с dxl - это то, что при DXLExport документ выгружается с аттачами, да еще и кодированными в Base64. Т.о. оверхед будет огромным, т.к. нам по сути требуется только поменять разметку в RT и не трогать $Files вообще. Чтобы не гонять $Files туда-обратно, можно аккуратно удалить $Files из документа перед экспортом и вернуть после импорта. Все эти манипуляции происходят без обращения к диску.
Единственное, чего я не смог придумать - как не использовать временный док. В приведенном ниже решении он создается и сохраняется(sic!) в текущей БД.

наколенка, не очень правильная с точки зрения дизайна, но суть передана верно
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">"многабукв"</div></div><div class="sp-body"><div class="sp-content">
Код:
Const XSLT = |<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ln="http://www.lotus.com/dxl">
<xsl:output
method="xml"
omit-xml-declaration="yes"
indent="no"/>
<xsl:param name="unid" select="'unid'"/>
<xsl:param name="replica" select="'replica'"/>
<xsl:template match="/">
<document form='tmpForm'>
<xsl:attribute name="replicaid"><xsl:value-of select="$replica" /></xsl:attribute>
<noteinfo><xsl:attribute name="unid"><xsl:value-of select="$unid" /></xsl:attribute></noteinfo>
<item name='Body'>
<richtext>
<pardef id='1'/>
<par def='1'>
<xsl:for-each select="//ln:attachmentref">
<xsl:copy-of select="."/>
</xsl:for-each>
</par>
</richtext>
</item>
</document>
</xsl:template>
</xsl:stylesheet>|

Class OmhProblem

Private t_fileItemName As String
Private t_stuffItemName As String
Private t_doc As NotesDocument
Private t_tmpDb As NotesDatabase

Private t_item As NotesRichTextItem

'
' @link https://codeby.net/ipb.html?s=&showtopic=42747&view=findpost&p=209313
' @param i_itemName - имя RT поля, которое нужно разобрать
' @param i_fileItemName - имя RT поля, в которое лягут файлы
' @param i_stuffItemName - имя RT поля, в которое ляжет все остальное
Sub new( i_doc As NotesDocument , i_itemName As String, i_fileItemName As String, i_stuffItemName As String )
Set t_doc = i_doc
Set t_item = t_doc.GetFirstItem( i_itemName )
t_fileItemName = i_fileItemName
t_stuffItemName = i_stuffItemName
Set t_tmpDb = t_doc.ParentDatabase '' в кач-ве временной базы лучше использовать какую-нить локальную БД
End Sub

Public Sub solve()
Call processStuff() ' текст
Call processFiles() ' аттачи

Call t_doc.Save( True, False )
End Sub

' формируем RT без аттачей. тут как бы проблем не возникает:
' копируем исходное поле во временный документ, валим все аттачи, копируем обратно
' нельзя использовать исходный документ, т.к. удаление аттача из RT удаляет $File.
Private Sub processStuff()
Dim doc As New NotesDocument( t_tmpDb )
Dim tmpItem As NotesRichTextItem
Set tmpItem = t_item.copyItemToDocument( doc , "Body" )
Forall eo In tmpItem.EmbeddedObjects
If ( eo.Type = EMBED_ATTACHMENT ) Then
Call eo.remove()
End If
End Forall
Call tmpItem.CopyItemToDocument( t_doc, t_stuffItemName )
End Sub

' формируем RT, содержащее только вложения исходного поля
' 1. копируем поле во временный документ 
' 2. удаляем $File через doc.GetAttachment().remove - этот метод не удаляет ссылки на файлы в RT. Т.е. имеем RT с "битыми ссылками" на аттачи
' 3 .прогоняем документ через XSL Transformer, убирая из RT все, кроме аттачей 
' 4. результирующий документ рендерим в указанное поле исходного документа.
' 
' шаг  2 (танцы с удалением $File) нужен для оптимизации использования DXL: 
' в противном случае через парсер пройдет контент файлов. Пара файлов по 10Mb вполне в состоянии уложить клиента.
Private Sub processFiles()
Dim doc As New NotesDocument( t_tmpDb )
Call t_item.copyItemToDocument( doc , "Body" )
Dim filenames As Variant
filenames = Evaluate("@AttachmentNames" , doc )
Forall filename In filenames
Call doc.GetAttachment(filename).remove()
End Forall
Set doc = doMagic( doc )

' копируем обратно $Files - без них падает copyItemToDocument
Call t_item.copyItemToDocument( doc , "TmpBody" )
Call safeRemoveRtField( "TmpBody" ) 

Call t_item.remove() ' чистим исходное поле

Call doc.GetFirstItem("Body").CopyItemToDocument( t_doc , t_fileItemName )
End Sub


Private Function doMagic(doc As NotesDocument) As NotesDocument
Dim session As New NotesSession
Call doc.Save(True, False ) ' сохранение нужно для того, чтобы найти документ после DXLImport.
Dim unid As String
unid = doc.UniversalID
Dim exporter As NotesDXLExporter
Set exporter = session.CreateDXLExporter(doc)
Dim xsl As NotesStream
Set xsl = session.CreateStream()
Call xsl.WriteText( XSLT ) 
Dim transformer As NotesXSLTransformer 
Set transformer=session.CreateXSLTransformer( exporter , xsl )
Call transformer.AddParameter("unid" , unid )
Call transformer.AddParameter("replica" , doc.ParentDatabase.ReplicaID)

Dim importer As NotesDXLImporter
Set importer = session.CreateDXLImporter( transformer , t_tmpDb )
importer.DocumentImportOption = DXLIMPORTOPTION_REPLACE_ELSE_CREATE
Call exporter.process
Delete doc
Set doMagic = t_tmpDb.GetDocumentByUNID( unid )
End Function

' простое удаление RT поля сносит $Files. 
' поэтому удалять RT следует примерно так:
Private Function safeRemoveRtField(itemName As String)
Const TMP_ITEM_NAME = "Killme"
Dim tmpItem As New NotesItem( t_doc , TMP_ITEM_NAME , "" )
Call t_doc.ReplaceItemValue(itemName , tmpItem )
Call t_doc.RemoveItem(itemName)
Call t_doc.RemoveItem(TMP_ITEM_NAME)
End Function
End Class
 

NickProstoNick

Статус как статус :)
Lotus Team
22.08.2008
1 851
27
BIT
0
turumbay
Спасибо!
Полезный код. Особенно по части удаления RT-поля:
Код:
Private Function safeRemoveRtField(itemName As String)
Const TMP_ITEM_NAME = "Killme"
Dim tmpItem As New NotesItem( t_doc , TMP_ITEM_NAME , "" )
Call t_doc.ReplaceItemValue(itemName , tmpItem )
Call t_doc.RemoveItem(itemName)
Call t_doc.RemoveItem(TMP_ITEM_NAME)
End Function
Что сделал я:
- cоздаю копию моего документа с аттачами (docMain);
- удаляю все поля из docMain, кроме $Files;
- формирую xml-строку временного документа в который дописываю теги attachmentref соответственно полям $Files из docMain;
- импорт временного документа
- дописываю RT-поле временного документа в docMain

Теги attachmentref для нескольких типов файлов загоняю в константы.
Такой метод мне показался менее геморройным по части парса xml.
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 933
609
BIT
177
turumbay я так понимаю "хитрости" по совмещению LS и трансформации вызваны опасениями оверхеда по памяти?
может вариант создания нового дока с SAX обработкой был бы менее затратным по ресурсам?!
ведь здесь по памяти >2-х раз гоняются файлы...
а так - да, подход интересный
 

Darkhan

Green Team
14.12.2012
99
2
BIT
0
Товарищи, а как насчет:
Код:
Call notesRichTextItem.AppendRTItem( notesRichTextItem2 )
?
 

ToxaRat

Чёрный маг
Green Team
06.11.2007
3 332
42
BIT
0
а в чём трабла через DXML узнать какие аттачи в нужном РТ и отдельно изолировать их?
или вообще сразу все аттачи изолировать?
 
T

turumbay

turumbay я так понимаю "хитрости" по совмещению LS и трансформации вызваны опасениями оверхеда по памяти?
"хитрости" с XSL затеяны для того, чтобы убрать из RT поля все, кроме аттачей. При этом сохраняются исходные иконки аттачей и все это - без выгрузки файлов на диск(т.е. security level = Don't allow restricted ops, что бывает важно для серверных агентов)
может вариант создания нового дока с SAX обработкой был бы менее затратным по ресурсам?!
ведь здесь по памяти >2-х раз гоняются файлы...
Файлы-то как раз не гоняются. Для этого используется вторая "хитрость": аттачи удаляются нафиг из документа перед экспортом и подкладываются обратно после импорта. Т.е. дерево DOM получается достаточно легковесным и его обработка не вносит сколько-нибудь значимой задержки. Кроме этого экономим на трафике и кодировании/декодировании в base64.
SAX ессно обыграет XSL, но есть подозрение, что с учетом приведенной выше оптимизации разница будет невелика. Реальный выигрыш можно будет почувствовать на документах, у которых само RT-поле содержит кучу разметки. Если в боди скопипащены html-версии второго тома "Войны и мира" и восемнадцатого издания таблиц Брадиса - я бы тоже подумал в сторону SAX
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 933
609
BIT
177
Код:
	Private Sub processStuff()
Dim doc As New NotesDocument( t_tmpDb )
Dim tmpItem As NotesRichTextItem
Set tmpItem = t_item.copyItemToDocument( doc , "Body" )
как один из примеров - аттачи гоняются в памяти
еще два раза в processFiles() (подобный код)
т.е. в инмомори док копируются (неоднократно) приатаченные файлы

яже предполагал - сразу гнать через SAX в новый док (однократно) со всеми сопутствующими коррекциями
 
T

turumbay

Код:
	Private Sub processStuff()
Dim doc As New NotesDocument( t_tmpDb )
Dim tmpItem As NotesRichTextItem
Set tmpItem = t_item.copyItemToDocument( doc , "Body" )
как один из примеров - аттачи гоняются в памяти
еще два раза в processFiles() (подобный код)
т.е. в инмомори док копируются (неоднократно) приатаченные файлы
При таком способе копирования контент аттача не передается на клиента, что легко проверяется снифером. Т.е. ни трафика ни памяти данный код не жрет...

updated: а вот если "сразу гнать через SAX" - то файл сначала скачается с сервера, а затем еще и закодируется в base64. Поэтому имеет смысл "отцепить" файлы от документа перед выгрузкой...
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 933
609
BIT
177
я немного не о том (хотя аспект сетевого трафика небезинтересен -_- ), а именно о памяти, например - в упомянутом случае серверного кода
что бывает важно для серверных агентов
как я себе это представлял - полное содержимое поля (в т.ч. и с файлами) копируется в инстанс нового дока
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!