Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "oox/core/xmlfilterbase.hxx"
21 :
22 : #include <cstdio>
23 : #include <set>
24 : #include <com/sun/star/container/XNameContainer.hpp>
25 : #include <com/sun/star/embed/XRelationshipAccess.hpp>
26 : #include <com/sun/star/xml/sax/InputSource.hpp>
27 : #include <com/sun/star/xml/sax/XFastParser.hpp>
28 : #include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
29 : #include <com/sun/star/document/XDocumentProperties.hpp>
30 : #include <unotools/mediadescriptor.hxx>
31 : #include <sax/fshelper.hxx>
32 : #include <rtl/strbuf.hxx>
33 : #include <rtl/ustrbuf.hxx>
34 : #include <rtl/instance.hxx>
35 : #include <i18nlangtag/languagetag.hxx>
36 : #include "oox/core/fastparser.hxx"
37 : #include "oox/core/filterdetect.hxx"
38 : #include "oox/core/fragmenthandler.hxx"
39 : #include "oox/core/recordparser.hxx"
40 : #include "oox/core/relationshandler.hxx"
41 : #include "oox/helper/containerhelper.hxx"
42 : #include "oox/helper/propertyset.hxx"
43 : #include "oox/helper/zipstorage.hxx"
44 : #include "oox/token/properties.hxx"
45 : #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
46 : #include <com/sun/star/document/XOOXMLDocumentPropertiesImporter.hpp>
47 : #include <com/sun/star/xml/dom/XDocument.hpp>
48 : #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
49 : #include <comphelper/processfactory.hxx>
50 : #include <oox/core/filterdetect.hxx>
51 : #include <comphelper/storagehelper.hxx>
52 :
53 : #include <oox/crypto/DocumentEncryption.hxx>
54 :
55 : using ::com::sun::star::xml::dom::DocumentBuilder;
56 : using ::com::sun::star::xml::dom::XDocument;
57 : using ::com::sun::star::xml::dom::XDocumentBuilder;
58 :
59 : namespace oox {
60 : namespace core {
61 :
62 : using namespace ::com::sun::star;
63 : using namespace ::com::sun::star::beans;
64 : using namespace ::com::sun::star::container;
65 : using namespace ::com::sun::star::document;
66 : using namespace ::com::sun::star::embed;
67 : using namespace ::com::sun::star::io;
68 : using namespace ::com::sun::star::lang;
69 : using namespace ::com::sun::star::uno;
70 : using namespace ::com::sun::star::xml::sax;
71 :
72 : using utl::MediaDescriptor;
73 : using ::sax_fastparser::FSHelperPtr;
74 : using ::sax_fastparser::FastSerializerHelper;
75 :
76 : namespace {
77 :
78 0 : bool lclHasSuffix( const OUString& rFragmentPath, const OUString& rSuffix )
79 : {
80 0 : sal_Int32 nSuffixPos = rFragmentPath.getLength() - rSuffix.getLength();
81 0 : return (nSuffixPos >= 0) && rFragmentPath.match( rSuffix, nSuffixPos );
82 : }
83 :
84 : struct NamespaceIds: public rtl::StaticWithInit<
85 : Sequence< beans::Pair< OUString, sal_Int32 > >,
86 : NamespaceIds>
87 : {
88 0 : Sequence< beans::Pair< OUString, sal_Int32 > > operator()()
89 : {
90 : static const char* const namespaceURIs[] = {
91 : "http://www.w3.org/XML/1998/namespace",
92 : "http://schemas.openxmlformats.org/package/2006/relationships",
93 : "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
94 : "http://purl.oclc.org/ooxml/officeDocument/relationships",
95 : "http://schemas.openxmlformats.org/drawingml/2006/main",
96 : "http://purl.oclc.org/ooxml/drawingml/main",
97 : "http://schemas.openxmlformats.org/drawingml/2006/diagram",
98 : "http://purl.oclc.org/ooxml/drawingml/diagram",
99 : "http://schemas.openxmlformats.org/drawingml/2006/chart",
100 : "http://schemas.openxmlformats.org/drawingml/2006/chartDrawing",
101 : "urn:schemas-microsoft-com:vml",
102 : "urn:schemas-microsoft-com:office:office",
103 : "urn:schemas-microsoft-com:office:word",
104 : "urn:schemas-microsoft-com:office:excel",
105 : "urn:schemas-microsoft-com:office:powerpoint",
106 : "http://schemas.microsoft.com/office/2006/activeX",
107 : "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
108 : "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing",
109 : "http://schemas.microsoft.com/office/excel/2006/main",
110 : "http://schemas.openxmlformats.org/presentationml/2006/main",
111 : "http://schemas.openxmlformats.org/markup-compatibility/2006",
112 : "http://schemas.openxmlformats.org/spreadsheetml/2006/main/v2",
113 : "http://schemas.microsoft.com/office/drawing/2008/diagram",
114 : "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
115 : };
116 :
117 : static const sal_Int32 namespaceIds[] = {
118 : NMSP_xml,
119 : NMSP_packageRel,
120 : NMSP_officeRel,
121 : NMSP_officeRel,
122 : NMSP_dml,
123 : NMSP_dml,
124 : NMSP_dmlDiagram,
125 : NMSP_dmlDiagram,
126 : NMSP_dmlChart,
127 : NMSP_dmlChartDr,
128 : NMSP_vml,
129 : NMSP_vmlOffice,
130 : NMSP_vmlWord,
131 : NMSP_vmlExcel,
132 : NMSP_vmlPowerpoint,
133 : NMSP_ax,
134 : NMSP_xls,
135 : NMSP_xm,
136 : NMSP_dmlSpreadDr,
137 : NMSP_ppt,
138 : NMSP_mce,
139 : NMSP_mceTest,
140 : NMSP_dsp,
141 : NMSP_xlsExtLst
142 : };
143 :
144 0 : Sequence< beans::Pair< OUString, sal_Int32 > > aRet(STATIC_ARRAY_SIZE(namespaceIds));
145 0 : for( sal_Int32 i=0; i<aRet.getLength(); ++i )
146 0 : aRet[i] = make_Pair(
147 0 : OUString::createFromAscii(namespaceURIs[i]),
148 0 : namespaceIds[i]);
149 0 : return aRet;
150 : }
151 : };
152 :
153 0 : void registerNamespaces( FastParser& rParser )
154 : {
155 0 : const Sequence< beans::Pair<OUString, sal_Int32> > ids = NamespaceIds::get();
156 :
157 : // Filter out duplicates: a namespace can have multiple URL's, think of
158 : // strict vs trasitional.
159 0 : std::set<sal_Int32> aSet;
160 0 : for (sal_Int32 i = 0; i < ids.getLength(); ++i)
161 0 : aSet.insert(ids[i].Second);
162 :
163 0 : for (std::set<sal_Int32>::iterator it = aSet.begin(); it != aSet.end(); ++it)
164 0 : rParser.registerNamespace(*it);
165 0 : }
166 :
167 : } // namespace
168 :
169 : struct XmlFilterBaseImpl
170 : {
171 : typedef RefMap< OUString, Relations > RelationsMap;
172 :
173 : FastParser maFastParser;
174 : const OUString maBinSuffix;
175 : const OUString maVmlSuffix;
176 : RelationsMap maRelationsMap;
177 : TextFieldStack maTextFieldStack;
178 :
179 : explicit XmlFilterBaseImpl( const Reference< XComponentContext >& rxContext ) throw( RuntimeException );
180 : ~XmlFilterBaseImpl();
181 : };
182 :
183 0 : XmlFilterBaseImpl::XmlFilterBaseImpl( const Reference< XComponentContext >& rxContext ) throw( RuntimeException ) :
184 : maFastParser( rxContext ),
185 : maBinSuffix( ".bin" ),
186 0 : maVmlSuffix( ".vml" )
187 : {
188 : // register XML namespaces
189 0 : registerNamespaces(maFastParser);
190 0 : }
191 :
192 0 : XmlFilterBaseImpl::~XmlFilterBaseImpl()
193 : {
194 0 : }
195 :
196 0 : XmlFilterBase::XmlFilterBase( const Reference< XComponentContext >& rxContext ) throw( RuntimeException ) :
197 : FilterBase( rxContext ),
198 0 : mxImpl( new XmlFilterBaseImpl( rxContext ) ),
199 : mnRelId( 1 ),
200 0 : mnMaxDocId( 0 )
201 : {
202 0 : }
203 :
204 0 : XmlFilterBase::~XmlFilterBase()
205 : {
206 : // #i118640# Reset the DocumentHandler at the FastSaxParser manually; this is
207 : // needed since the mechanism is that instances of FragmentHandler execute
208 : // their stuff (creating objects, setting attributes, ...) on being destroyed.
209 : // They get destroyed by setting a new DocumentHandler. This also happens in
210 : // the following implicit destruction chain of ~XmlFilterBaseImpl, but in that
211 : // case it's member RelationsMap maRelationsMap will be destroyed, but maybe
212 : // still be used by ~FragmentHandler -> crash.
213 0 : mxImpl->maFastParser.setDocumentHandler( 0 );
214 0 : }
215 :
216 :
217 :
218 0 : void XmlFilterBase::importDocumentProperties()
219 : {
220 0 : Reference< XMultiServiceFactory > xFactory( getComponentContext()->getServiceManager(), UNO_QUERY );
221 0 : MediaDescriptor aMediaDesc( getMediaDescriptor() );
222 0 : Reference< XInputStream > xInputStream;
223 0 : Reference< XComponentContext > xContext = getComponentContext();
224 0 : ::oox::core::FilterDetect aDetector( xContext );
225 0 : xInputStream = aDetector.extractUnencryptedPackage( aMediaDesc );
226 0 : Reference< XComponent > xModel( getModel(), UNO_QUERY );
227 : Reference< XStorage > xDocumentStorage (
228 0 : ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( OFOPXML_STORAGE_FORMAT_STRING, xInputStream ) );
229 0 : Reference< XInterface > xTemp = xContext->getServiceManager()->createInstanceWithContext(
230 : "com.sun.star.document.OOXMLDocumentPropertiesImporter",
231 0 : xContext);
232 0 : Reference< XOOXMLDocumentPropertiesImporter > xImporter( xTemp, UNO_QUERY );
233 0 : Reference< XDocumentPropertiesSupplier > xPropSupplier( xModel, UNO_QUERY);
234 0 : xImporter->importProperties( xDocumentStorage, xPropSupplier->getDocumentProperties() );
235 0 : }
236 :
237 0 : FastParser* XmlFilterBase::createParser() const
238 : {
239 0 : FastParser* pParser = new FastParser(getComponentContext());
240 0 : registerNamespaces(*pParser);
241 0 : return pParser;
242 : }
243 :
244 : namespace {
245 :
246 0 : OUString getTransitionalRelationshipOfficeDocType(const OUString& rPart)
247 : {
248 0 : static const OUString aBase("http://schemas.openxmlformats.org/officeDocument/2006/relationships/");
249 0 : return aBase + rPart;
250 : }
251 :
252 0 : OUString getStrictRelationshipOfficeDocType(const OUString& rPart)
253 : {
254 0 : static const OUString aBase("http://purl.oclc.org/ooxml/officeDocument/relationships/");
255 0 : return aBase + rPart;
256 : }
257 :
258 : }
259 :
260 0 : OUString XmlFilterBase::getFragmentPathFromFirstTypeFromOfficeDoc( const OUString& rPart )
261 : {
262 : // importRelations() caches the relations map for subsequence calls
263 0 : const OUString aTransitionalRelationshipType = getTransitionalRelationshipOfficeDocType(rPart);
264 0 : OUString aFragment = importRelations( OUString() )->getFragmentPathFromFirstType( aTransitionalRelationshipType );
265 0 : if(aFragment.isEmpty())
266 : {
267 0 : const OUString aStrictRelationshipType = getStrictRelationshipOfficeDocType(rPart);
268 0 : aFragment = importRelations( OUString() )->getFragmentPathFromFirstType( aStrictRelationshipType );
269 : }
270 :
271 0 : return aFragment;
272 : }
273 :
274 0 : bool XmlFilterBase::importFragment( const rtl::Reference<FragmentHandler>& rxHandler )
275 : {
276 0 : return importFragment(rxHandler, mxImpl->maFastParser);
277 : }
278 :
279 0 : bool XmlFilterBase::importFragment( const rtl::Reference<FragmentHandler>& rxHandler, FastParser& rParser )
280 : {
281 : OSL_ENSURE( rxHandler.is(), "XmlFilterBase::importFragment - missing fragment handler" );
282 0 : if( !rxHandler.is() )
283 0 : return false;
284 :
285 : // fragment handler must contain path to fragment stream
286 0 : OUString aFragmentPath = rxHandler->getFragmentPath();
287 : OSL_ENSURE( !aFragmentPath.isEmpty(), "XmlFilterBase::importFragment - missing fragment path" );
288 0 : if( aFragmentPath.isEmpty() )
289 0 : return false;
290 :
291 : // try to import binary streams (fragment extension must be '.bin')
292 0 : if( lclHasSuffix( aFragmentPath, mxImpl->maBinSuffix ) )
293 : {
294 : try
295 : {
296 : // try to open the fragment stream (this may fail - do not assert)
297 0 : Reference< XInputStream > xInStrm( openInputStream( aFragmentPath ), UNO_SET_THROW );
298 :
299 : // create the record parser
300 0 : RecordParser aParser;
301 0 : aParser.setFragmentHandler( rxHandler );
302 :
303 : // create the input source and parse the stream
304 0 : RecordInputSource aSource;
305 0 : aSource.mxInStream.reset( new BinaryXInputStream( xInStrm, true ) );
306 0 : aSource.maSystemId = aFragmentPath;
307 0 : aParser.parseStream( aSource );
308 0 : return true;
309 : }
310 0 : catch( Exception& )
311 : {
312 : }
313 0 : return false;
314 : }
315 :
316 : // get the XFastDocumentHandler interface from the fragment handler
317 0 : Reference< XFastDocumentHandler > xDocHandler( rxHandler.get() );
318 0 : if( !xDocHandler.is() )
319 0 : return false;
320 :
321 : // try to import XML stream
322 : try
323 : {
324 : /* Try to open the fragment stream (may fail, do not throw/assert).
325 : Using the virtual function openFragmentStream() allows a document
326 : handler to create specialized input streams, e.g. VML streams that
327 : have to preprocess the raw input data. */
328 0 : Reference< XInputStream > xInStrm = rxHandler->openFragmentStream();
329 :
330 : // own try/catch block for showing parser failure assertion with fragment path
331 0 : if( xInStrm.is() ) try
332 : {
333 0 : rParser.setDocumentHandler(xDocHandler);
334 0 : rParser.parseStream(xInStrm, aFragmentPath);
335 0 : return true;
336 : }
337 0 : catch( Exception& )
338 : {
339 : OSL_FAIL( OStringBuffer( "XmlFilterBase::importFragment - XML parser failed in fragment '" ).
340 : append( OUStringToOString( aFragmentPath, RTL_TEXTENCODING_ASCII_US ) ).append( '\'' ).getStr() );
341 0 : }
342 : }
343 0 : catch( Exception& )
344 : {
345 : }
346 0 : return false;
347 : }
348 :
349 0 : OUString XmlFilterBase::getNamespaceURL( const OUString& rPrefix )
350 : {
351 0 : return mxImpl->maFastParser.getNamespaceURL( rPrefix );
352 : }
353 :
354 0 : bool XmlFilterBase::hasNamespaceURL( const OUString& rPrefix ) const
355 : {
356 0 : return mxImpl->maFastParser.hasNamespaceURL(rPrefix);
357 : }
358 :
359 0 : sal_Int32 XmlFilterBase::getNamespaceId( const OUString& rUrl )
360 : {
361 0 : return mxImpl->maFastParser.getNamespaceId( rUrl );
362 : }
363 :
364 0 : Reference<XDocument> XmlFilterBase::importFragment( const OUString& aFragmentPath )
365 : {
366 0 : Reference<XDocument> xRet;
367 :
368 : // path to fragment stream valid?
369 : OSL_ENSURE( !aFragmentPath.isEmpty(), "XmlFilterBase::importFragment - empty fragment path" );
370 0 : if( aFragmentPath.isEmpty() )
371 0 : return xRet;
372 :
373 : // try to open the fragment stream (this may fail - do not assert)
374 0 : Reference< XInputStream > xInStrm = openInputStream( aFragmentPath );
375 0 : if( !xInStrm.is() )
376 0 : return xRet;
377 :
378 : // binary streams (fragment extension is '.bin') currently not supported
379 0 : sal_Int32 nBinSuffixPos = aFragmentPath.getLength() - mxImpl->maBinSuffix.getLength();
380 0 : if( (nBinSuffixPos >= 0) && aFragmentPath.match( mxImpl->maBinSuffix, nBinSuffixPos ) )
381 0 : return xRet;
382 :
383 : // try to import XML stream
384 : try
385 : {
386 : // create the dom parser
387 0 : Reference<XDocumentBuilder> xDomBuilder( DocumentBuilder::create( getComponentContext() ) );
388 :
389 : // create DOM from fragment
390 0 : xRet = xDomBuilder->parse(xInStrm);
391 : }
392 0 : catch( Exception& )
393 : {
394 : }
395 :
396 0 : return xRet;
397 : }
398 :
399 0 : bool XmlFilterBase::importFragment( const ::rtl::Reference< FragmentHandler >& rxHandler,
400 : const Reference< XFastSAXSerializable >& rxSerializer )
401 : {
402 0 : Reference< XFastDocumentHandler > xDocHandler( rxHandler.get() );
403 0 : if( !xDocHandler.is() )
404 0 : return false;
405 :
406 : // try to import XML stream
407 : try
408 : {
409 0 : rxSerializer->fastSerialize( xDocHandler,
410 0 : mxImpl->maFastParser.getTokenHandler(),
411 : Sequence< StringPair >(),
412 0 : NamespaceIds::get() );
413 0 : return true;
414 : }
415 0 : catch( Exception& )
416 : {}
417 :
418 0 : return false;
419 : }
420 :
421 0 : RelationsRef XmlFilterBase::importRelations( const OUString& rFragmentPath )
422 : {
423 : // try to find cached relations
424 0 : RelationsRef& rxRelations = mxImpl->maRelationsMap[ rFragmentPath ];
425 0 : if( !rxRelations )
426 : {
427 : // import and cache relations
428 0 : rxRelations.reset( new Relations( rFragmentPath ) );
429 0 : importFragment( new RelationsFragment( *this, rxRelations ) );
430 : }
431 0 : return rxRelations;
432 : }
433 :
434 0 : Reference< XOutputStream > XmlFilterBase::openFragmentStream( const OUString& rStreamName, const OUString& rMediaType )
435 : {
436 0 : Reference< XOutputStream > xOutputStream = openOutputStream( rStreamName );
437 0 : PropertySet aPropSet( xOutputStream );
438 0 : aPropSet.setProperty( PROP_MediaType, rMediaType );
439 0 : return xOutputStream;
440 : }
441 :
442 0 : FSHelperPtr XmlFilterBase::openFragmentStreamWithSerializer( const OUString& rStreamName, const OUString& rMediaType )
443 : {
444 0 : bool bWriteHeader = true;
445 0 : if( rMediaType.indexOfAsciiL( "vml", 3 ) >= 0 &&
446 0 : rMediaType.indexOfAsciiL( "+xml", 4 ) < 0 )
447 0 : bWriteHeader = false;
448 0 : return FSHelperPtr( new FastSerializerHelper( openFragmentStream( rStreamName, rMediaType ), bWriteHeader ) );
449 : }
450 :
451 0 : TextFieldStack& XmlFilterBase::getTextFieldStack() const
452 : {
453 0 : return mxImpl->maTextFieldStack;
454 : }
455 :
456 : namespace {
457 :
458 0 : OUString lclAddRelation( const Reference< XRelationshipAccess > xRelations, sal_Int32 nId, const OUString& rType, const OUString& rTarget, bool bExternal )
459 : {
460 0 : OUString sId = OUStringBuffer().appendAscii( "rId" ).append( nId ).makeStringAndClear();
461 :
462 0 : Sequence< StringPair > aEntry( bExternal ? 3 : 2 );
463 0 : aEntry[0].First = "Type";
464 0 : aEntry[0].Second = rType;
465 0 : aEntry[1].First = "Target";
466 0 : aEntry[1].Second = rTarget;
467 0 : if( bExternal )
468 : {
469 0 : aEntry[2].First = "TargetMode";
470 0 : aEntry[2].Second = "External";
471 : }
472 0 : xRelations->insertRelationshipByID( sId, aEntry, sal_True );
473 :
474 0 : return sId;
475 : }
476 :
477 : } // namespace
478 :
479 0 : OUString XmlFilterBase::addRelation( const OUString& rType, const OUString& rTarget, bool bExternal )
480 : {
481 0 : Reference< XRelationshipAccess > xRelations( getStorage()->getXStorage(), UNO_QUERY );
482 0 : if( xRelations.is() )
483 0 : return lclAddRelation( xRelations, mnRelId ++, rType, rTarget, bExternal );
484 :
485 0 : return OUString();
486 : }
487 :
488 0 : OUString XmlFilterBase::addRelation( const Reference< XOutputStream > xOutputStream, const OUString& rType, const OUString& rTarget, bool bExternal )
489 : {
490 0 : sal_Int32 nId = 0;
491 :
492 0 : PropertySet aPropSet( xOutputStream );
493 0 : if( aPropSet.is() )
494 0 : aPropSet.getProperty( nId, PROP_RelId );
495 : else
496 0 : nId = mnRelId++;
497 :
498 0 : Reference< XRelationshipAccess > xRelations( xOutputStream, UNO_QUERY );
499 0 : if( xRelations.is() )
500 0 : return lclAddRelation( xRelations, nId, rType, rTarget, bExternal );
501 :
502 0 : return OUString();
503 : }
504 :
505 : static void
506 0 : writeElement( FSHelperPtr pDoc, sal_Int32 nXmlElement, const OUString& sValue )
507 : {
508 0 : if( sValue.isEmpty() )
509 0 : return;
510 0 : pDoc->startElement( nXmlElement, FSEND );
511 0 : pDoc->writeEscaped( sValue );
512 0 : pDoc->endElement( nXmlElement );
513 : }
514 :
515 : static void
516 0 : writeElement( FSHelperPtr pDoc, sal_Int32 nXmlElement, const sal_Int32 nValue )
517 : {
518 0 : pDoc->startElement( nXmlElement, FSEND );
519 0 : pDoc->write( OUString::number( nValue ) );
520 0 : pDoc->endElement( nXmlElement );
521 0 : }
522 :
523 : static void
524 0 : writeElement( FSHelperPtr pDoc, sal_Int32 nXmlElement, const util::DateTime& rTime )
525 : {
526 0 : if( rTime.Year == 0 )
527 0 : return;
528 :
529 0 : if ( ( nXmlElement >> 16 ) != XML_dcterms )
530 0 : pDoc->startElement( nXmlElement, FSEND );
531 : else
532 : pDoc->startElement( nXmlElement,
533 : FSNS( XML_xsi, XML_type ), "dcterms:W3CDTF",
534 0 : FSEND );
535 :
536 : char pStr[200];
537 : snprintf( pStr, sizeof( pStr ), "%d-%02d-%02dT%02d:%02d:%02dZ",
538 : rTime.Year, rTime.Month, rTime.Day,
539 0 : rTime.Hours, rTime.Minutes, rTime.Seconds );
540 :
541 0 : pDoc->write( pStr );
542 :
543 0 : pDoc->endElement( nXmlElement );
544 : }
545 :
546 : static void
547 0 : writeElement( FSHelperPtr pDoc, sal_Int32 nXmlElement, Sequence< OUString > aItems )
548 : {
549 0 : if( aItems.getLength() == 0 )
550 0 : return;
551 :
552 0 : OUStringBuffer sRep;
553 0 : sRep.append( aItems[ 0 ] );
554 :
555 0 : for( sal_Int32 i = 1, end = aItems.getLength(); i < end; ++i )
556 : {
557 0 : sRep.appendAscii( " " ).append( aItems[ i ] );
558 : }
559 :
560 0 : writeElement( pDoc, nXmlElement, sRep.makeStringAndClear() );
561 : }
562 :
563 : static void
564 0 : writeElement( FSHelperPtr pDoc, sal_Int32 nXmlElement, const LanguageTag& rLanguageTag )
565 : {
566 : // dc:language, Dublin Core recommends "such as RFC 4646", which is BCP 47
567 : // and obsoleted by RFC 5646, see
568 : // http://dublincore.org/documents/dcmi-terms/#terms-language
569 : // http://dublincore.org/documents/dcmi-terms/#elements-language
570 0 : writeElement( pDoc, nXmlElement, rLanguageTag.getBcp47() );
571 0 : }
572 :
573 : static void
574 0 : writeCoreProperties( XmlFilterBase& rSelf, Reference< XDocumentProperties > xProperties )
575 : {
576 0 : OUString sValue;
577 0 : if( rSelf.getVersion() == oox::core::ISOIEC_29500_2008 )
578 0 : sValue = "http://schemas.openxmlformats.org/officedocument/2006/relationships/metadata/core-properties";
579 : else
580 0 : sValue = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";
581 :
582 0 : rSelf.addRelation( sValue, "docProps/core.xml" );
583 : FSHelperPtr pCoreProps = rSelf.openFragmentStreamWithSerializer(
584 : "docProps/core.xml",
585 0 : "application/vnd.openxmlformats-package.core-properties+xml" );
586 : pCoreProps->startElementNS( XML_cp, XML_coreProperties,
587 : FSNS( XML_xmlns, XML_cp ), "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
588 : FSNS( XML_xmlns, XML_dc ), "http://purl.org/dc/elements/1.1/",
589 : FSNS( XML_xmlns, XML_dcterms ), "http://purl.org/dc/terms/",
590 : FSNS( XML_xmlns, XML_dcmitype ), "http://purl.org/dc/dcmitype/",
591 : FSNS( XML_xmlns, XML_xsi ), "http://www.w3.org/2001/XMLSchema-instance",
592 0 : FSEND );
593 :
594 : #ifdef OOXTODO
595 : writeElement( pCoreProps, FSNS( XML_cp, XML_category ), "category" );
596 : writeElement( pCoreProps, FSNS( XML_cp, XML_contentStatus ), "status" );
597 : writeElement( pCoreProps, FSNS( XML_cp, XML_contentType ), "contentType" );
598 : #endif /* def OOXTODO */
599 0 : writeElement( pCoreProps, FSNS( XML_dcterms, XML_created ), xProperties->getCreationDate() );
600 0 : writeElement( pCoreProps, FSNS( XML_dc, XML_creator ), xProperties->getAuthor() );
601 0 : writeElement( pCoreProps, FSNS( XML_dc, XML_description ), xProperties->getDescription() );
602 : #ifdef OOXTODO
603 : writeElement( pCoreProps, FSNS( XML_dc, XML_identifier ), "ident" );
604 : #endif /* def OOXTODO */
605 0 : writeElement( pCoreProps, FSNS( XML_cp, XML_keywords ), xProperties->getKeywords() );
606 0 : writeElement( pCoreProps, FSNS( XML_dc, XML_language ), LanguageTag( xProperties->getLanguage()) );
607 0 : writeElement( pCoreProps, FSNS( XML_cp, XML_lastModifiedBy ), xProperties->getModifiedBy() );
608 0 : writeElement( pCoreProps, FSNS( XML_cp, XML_lastPrinted ), xProperties->getPrintDate() );
609 0 : writeElement( pCoreProps, FSNS( XML_dcterms, XML_modified ), xProperties->getModificationDate() );
610 0 : writeElement( pCoreProps, FSNS( XML_cp, XML_revision ), xProperties->getEditingCycles() );
611 0 : writeElement( pCoreProps, FSNS( XML_dc, XML_subject ), xProperties->getSubject() );
612 0 : writeElement( pCoreProps, FSNS( XML_dc, XML_title ), xProperties->getTitle() );
613 : #ifdef OOXTODO
614 : writeElement( pCoreProps, FSNS( XML_cp, XML_version ), "version" );
615 : #endif /* def OOXTODO */
616 :
617 0 : pCoreProps->endElementNS( XML_cp, XML_coreProperties );
618 0 : }
619 :
620 : static void
621 0 : writeAppProperties( XmlFilterBase& rSelf, Reference< XDocumentProperties > xProperties )
622 : {
623 : rSelf.addRelation(
624 : "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties",
625 0 : "docProps/app.xml" );
626 : FSHelperPtr pAppProps = rSelf.openFragmentStreamWithSerializer(
627 : "docProps/app.xml",
628 0 : "application/vnd.openxmlformats-officedocument.extended-properties+xml" );
629 : pAppProps->startElement( XML_Properties,
630 : XML_xmlns, "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
631 : FSNS( XML_xmlns, XML_vt ), "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes",
632 0 : FSEND );
633 :
634 0 : writeElement( pAppProps, XML_Template, xProperties->getTemplateName() );
635 : #ifdef OOXTODO
636 : writeElement( pAppProps, XML_Manager, "manager" );
637 : writeElement( pAppProps, XML_Company, "company" );
638 : writeElement( pAppProps, XML_Pages, "pages" );
639 : writeElement( pAppProps, XML_Words, "words" );
640 : writeElement( pAppProps, XML_Characters, "characters" );
641 : writeElement( pAppProps, XML_PresentationFormat, "presentation format" );
642 : writeElement( pAppProps, XML_Lines, "lines" );
643 : writeElement( pAppProps, XML_Slides, "slides" );
644 : writeElement( pAppProps, XML_Notes, "notes" );
645 : #endif /* def OOXTODO */
646 0 : writeElement( pAppProps, XML_TotalTime, xProperties->getEditingDuration() );
647 : #ifdef OOXTODO
648 : writeElement( pAppProps, XML_HiddenSlides, "hidden slides" );
649 : writeElement( pAppProps, XML_MMClips, "mm clips" );
650 : writeElement( pAppProps, XML_ScaleCrop, "scale crop" );
651 : writeElement( pAppProps, XML_HeadingPairs, "heading pairs" );
652 : writeElement( pAppProps, XML_TitlesOfParts, "titles of parts" );
653 : writeElement( pAppProps, XML_LinksUpToDate, "links up-to-date" );
654 : writeElement( pAppProps, XML_CharactersWithSpaces, "characters with spaces" );
655 : writeElement( pAppProps, XML_SharedDoc, "shared doc" );
656 : writeElement( pAppProps, XML_HyperlinkBase, "hyperlink base" );
657 : writeElement( pAppProps, XML_HLinks, "hlinks" );
658 : writeElement( pAppProps, XML_HyperlinksChanged, "hyperlinks changed" );
659 : writeElement( pAppProps, XML_DigSig, "digital signature" );
660 : #endif /* def OOXTODO */
661 0 : writeElement( pAppProps, XML_Application, xProperties->getGenerator() );
662 : #ifdef OOXTODO
663 : writeElement( pAppProps, XML_AppVersion, "app version" );
664 : writeElement( pAppProps, XML_DocSecurity, "doc security" );
665 : #endif /* def OOXTODO */
666 :
667 0 : uno::Sequence<beans::NamedValue> aStats = xProperties->getDocumentStatistics();
668 0 : for (sal_Int32 i = 0; i < aStats.getLength(); ++i)
669 : {
670 0 : if (aStats[i].Name == "ParagraphCount")
671 : {
672 0 : sal_Int32 nValue = 0;
673 0 : if (aStats[i].Value >>= nValue)
674 : {
675 0 : writeElement(pAppProps, XML_Paragraphs, OUString::number(nValue));
676 0 : break;
677 : }
678 : }
679 : }
680 :
681 0 : pAppProps->endElement( XML_Properties );
682 0 : }
683 :
684 0 : XmlFilterBase& XmlFilterBase::exportDocumentProperties( Reference< XDocumentProperties > xProperties )
685 : {
686 0 : if( xProperties.is() )
687 : {
688 0 : writeCoreProperties( *this, xProperties );
689 0 : writeAppProperties( *this, xProperties );
690 : }
691 0 : return *this;
692 : }
693 :
694 : // protected ------------------------------------------------------------------
695 :
696 0 : Reference< XInputStream > XmlFilterBase::implGetInputStream( MediaDescriptor& rMediaDesc ) const
697 : {
698 : /* Get the input stream directly from the media descriptor, or decrypt the
699 : package again. The latter is needed e.g. when the document is reloaded.
700 : All this is implemented in the detector service. */
701 0 : FilterDetect aDetector( getComponentContext() );
702 0 : return aDetector.extractUnencryptedPackage( rMediaDesc );
703 : }
704 :
705 0 : Reference<XStream> XmlFilterBase::implGetOutputStream( MediaDescriptor& rMediaDescriptor ) const
706 : {
707 0 : Sequence< NamedValue > aMediaEncData;
708 0 : aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault(
709 0 : MediaDescriptor::PROP_ENCRYPTIONDATA(),
710 0 : Sequence< NamedValue >() );
711 :
712 0 : OUString aPassword;
713 0 : for (int i=0; i<aMediaEncData.getLength(); i++)
714 : {
715 0 : if (aMediaEncData[i].Name == "OOXPassword")
716 : {
717 0 : Any& any = aMediaEncData[i].Value;
718 0 : any >>= aPassword;
719 0 : break;
720 : }
721 : }
722 0 : if (aPassword.isEmpty())
723 : {
724 0 : return FilterBase::implGetOutputStream( rMediaDescriptor );
725 : }
726 : else // We need to encrypt the stream so create a memory stream
727 : {
728 0 : Reference< XComponentContext > xContext = getComponentContext();
729 : return Reference< XStream > (
730 0 : xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.MemoryStream", xContext),
731 0 : uno::UNO_QUERY_THROW );
732 0 : }
733 : }
734 :
735 0 : bool XmlFilterBase::implFinalizeExport( MediaDescriptor& rMediaDescriptor )
736 : {
737 0 : bool bRet = true;
738 :
739 0 : Sequence< NamedValue > aMediaEncData;
740 0 : aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault(
741 0 : MediaDescriptor::PROP_ENCRYPTIONDATA(),
742 0 : Sequence< NamedValue >() );
743 :
744 0 : OUString aPassword;
745 :
746 0 : for (int i=0; i<aMediaEncData.getLength(); i++)
747 : {
748 0 : if (aMediaEncData[i].Name == "OOXPassword")
749 : {
750 0 : Any& any = aMediaEncData[i].Value;
751 0 : any >>= aPassword;
752 0 : break;
753 : }
754 : }
755 :
756 0 : if (!aPassword.isEmpty())
757 : {
758 0 : commitStorage();
759 :
760 0 : Reference< XStream> xDocumentStream (FilterBase::implGetOutputStream(rMediaDescriptor));
761 0 : oox::ole::OleStorage aOleStorage( getComponentContext(), xDocumentStream, true );
762 0 : DocumentEncryption encryptor(getMainDocumentStream(), aOleStorage, aPassword);
763 0 : bRet = encryptor.encrypt();
764 0 : if (bRet)
765 0 : aOleStorage.commit();
766 : }
767 :
768 0 : return bRet;
769 : }
770 :
771 : // private --------------------------------------------------------------------
772 :
773 0 : StorageRef XmlFilterBase::implCreateStorage( const Reference< XInputStream >& rxInStream ) const
774 : {
775 0 : return StorageRef( new ZipStorage( getComponentContext(), rxInStream ) );
776 : }
777 :
778 0 : StorageRef XmlFilterBase::implCreateStorage( const Reference< XStream >& rxOutStream ) const
779 : {
780 0 : return StorageRef( new ZipStorage( getComponentContext(), rxOutStream ) );
781 : }
782 :
783 :
784 :
785 : } // namespace core
786 0 : } // namespace oox
787 :
788 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|