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