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