1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 #include "oox/core/filterbase.hxx"
25
26 #include <set>
27 #include <com/sun/star/frame/XModel.hpp>
28 #include <com/sun/star/task/XStatusIndicator.hpp>
29 #include <com/sun/star/task/XInteractionHandler.hpp>
30 #include <comphelper/docpasswordhelper.hxx>
31 #include <comphelper/mediadescriptor.hxx>
32 #include <osl/mutex.hxx>
33 #include <rtl/instance.hxx>
34 #include <rtl/uri.hxx>
35 #include "oox/helper/binaryinputstream.hxx"
36 #include "oox/helper/binaryoutputstream.hxx"
37 #include "oox/helper/graphichelper.hxx"
38 #include "oox/helper/modelobjecthelper.hxx"
39 #include "oox/ole/oleobjecthelper.hxx"
40 #include "oox/ole/vbaproject.hxx"
41
42 namespace oox {
43 namespace core {
44
45 // ============================================================================
46
47 using namespace ::com::sun::star::beans;
48 using namespace ::com::sun::star::frame;
49 using namespace ::com::sun::star::graphic;
50 using namespace ::com::sun::star::io;
51 using namespace ::com::sun::star::lang;
52 using namespace ::com::sun::star::task;
53 using namespace ::com::sun::star::uno;
54
55 using ::comphelper::MediaDescriptor;
56 using ::comphelper::SequenceAsHashMap;
57 using ::oox::ole::OleObjectHelper;
58 using ::oox::ole::VbaProject;
59 using ::rtl::OUString;
60
61 // ============================================================================
62
63 namespace {
64
65 struct UrlPool
66 {
67 ::osl::Mutex maMutex;
68 ::std::set< OUString > maUrls;
69 };
70
71 struct StaticUrlPool : public ::rtl::Static< UrlPool, StaticUrlPool > {};
72
73 // ----------------------------------------------------------------------------
74
75 /** This guard prevents recursive loading/saving of the same document. */
76 class DocumentOpenedGuard
77 {
78 public:
79 explicit DocumentOpenedGuard( const OUString& rUrl );
80 ~DocumentOpenedGuard();
81
isValid() const82 inline bool isValid() const { return mbValid; }
83
84 private:
85 DocumentOpenedGuard( const DocumentOpenedGuard& );
86 DocumentOpenedGuard& operator=( const DocumentOpenedGuard& );
87
88 OUString maUrl;
89 bool mbValid;
90 };
91
DocumentOpenedGuard(const OUString & rUrl)92 DocumentOpenedGuard::DocumentOpenedGuard( const OUString& rUrl )
93 {
94 UrlPool& rUrlPool = StaticUrlPool::get();
95 ::osl::MutexGuard aGuard( rUrlPool.maMutex );
96 mbValid = (rUrl.getLength() == 0) || (rUrlPool.maUrls.count( rUrl ) == 0);
97 if( mbValid && (rUrl.getLength() > 0) )
98 {
99 rUrlPool.maUrls.insert( rUrl );
100 maUrl = rUrl;
101 }
102 }
103
~DocumentOpenedGuard()104 DocumentOpenedGuard::~DocumentOpenedGuard()
105 {
106 UrlPool& rUrlPool = StaticUrlPool::get();
107 ::osl::MutexGuard aGuard( rUrlPool.maMutex );
108 if( maUrl.getLength() > 0 )
109 rUrlPool.maUrls.erase( maUrl );
110 }
111
112 } // namespace
113
114 // ============================================================================
115
116 /** Specifies whether this filter is an import or export filter. */
117 enum FilterDirection
118 {
119 FILTERDIRECTION_UNKNOWN,
120 FILTERDIRECTION_IMPORT,
121 FILTERDIRECTION_EXPORT
122 };
123
124 // ----------------------------------------------------------------------------
125
126 struct FilterBaseImpl
127 {
128 typedef ::boost::shared_ptr< GraphicHelper > GraphicHelperRef;
129 typedef ::boost::shared_ptr< ModelObjectHelper > ModelObjHelperRef;
130 typedef ::boost::shared_ptr< OleObjectHelper > OleObjHelperRef;
131 typedef ::boost::shared_ptr< VbaProject > VbaProjectRef;
132
133 FilterDirection meDirection;
134 SequenceAsHashMap maArguments;
135 MediaDescriptor maMediaDesc;
136 OUString maFileUrl;
137 StorageRef mxStorage;
138
139 GraphicHelperRef mxGraphicHelper; /// Graphic and graphic object handling.
140 ModelObjHelperRef mxModelObjHelper; /// Tables to create new named drawing objects.
141 OleObjHelperRef mxOleObjHelper; /// OLE object handling.
142 VbaProjectRef mxVbaProject; /// VBA project manager.
143
144 Reference< XComponentContext > mxComponentContext;
145 Reference< XMultiComponentFactory > mxComponentFactory;
146 Reference< XMultiServiceFactory > mxServiceFactory;
147 Reference< XModel > mxModel;
148 Reference< XMultiServiceFactory > mxModelFactory;
149 Reference< XFrame > mxTargetFrame;
150 Reference< XInputStream > mxInStream;
151 Reference< XStream > mxOutStream;
152 Reference< XStatusIndicator > mxStatusIndicator;
153 Reference< XInteractionHandler > mxInteractionHandler;
154
155 explicit FilterBaseImpl( const Reference< XComponentContext >& rxContext ) throw( RuntimeException );
156
157 void setDocumentModel( const Reference< XComponent >& rxComponent ) throw( IllegalArgumentException );
158
159 void initializeFilter();
160 void finalizeFilter();
161 };
162
163 // ----------------------------------------------------------------------------
164
FilterBaseImpl(const Reference<XComponentContext> & rxContext)165 FilterBaseImpl::FilterBaseImpl( const Reference< XComponentContext >& rxContext ) throw( RuntimeException ) :
166 meDirection( FILTERDIRECTION_UNKNOWN ),
167 mxComponentContext( rxContext, UNO_SET_THROW ),
168 mxComponentFactory( rxContext->getServiceManager(), UNO_SET_THROW ),
169 mxServiceFactory( rxContext->getServiceManager(), UNO_QUERY_THROW )
170 {
171 }
172
setDocumentModel(const Reference<XComponent> & rxComponent)173 void FilterBaseImpl::setDocumentModel( const Reference< XComponent >& rxComponent ) throw( IllegalArgumentException )
174 {
175 try
176 {
177 mxModel.set( rxComponent, UNO_QUERY_THROW );
178 mxModelFactory.set( rxComponent, UNO_QUERY_THROW );
179 }
180 catch( Exception& )
181 {
182 throw IllegalArgumentException();
183 }
184 }
185
initializeFilter()186 void FilterBaseImpl::initializeFilter()
187 {
188 try
189 {
190 // lock the model controllers
191 mxModel->lockControllers();
192 }
193 catch( Exception& )
194 {
195 }
196 }
197
finalizeFilter()198 void FilterBaseImpl::finalizeFilter()
199 {
200 try
201 {
202 // write the descriptor back to the document model (adds the passwords)
203 mxModel->attachResource( maFileUrl, maMediaDesc.getAsConstPropertyValueList() );
204 // unlock the model controllers
205 mxModel->unlockControllers();
206 }
207 catch( Exception& )
208 {
209 }
210 }
211
212 // ============================================================================
213
FilterBase(const Reference<XComponentContext> & rxContext)214 FilterBase::FilterBase( const Reference< XComponentContext >& rxContext ) throw( RuntimeException ) :
215 mxImpl( new FilterBaseImpl( rxContext ) )
216 {
217 }
218
~FilterBase()219 FilterBase::~FilterBase()
220 {
221 }
222
isImportFilter() const223 bool FilterBase::isImportFilter() const
224 {
225 return mxImpl->meDirection == FILTERDIRECTION_IMPORT;
226 }
227
isExportFilter() const228 bool FilterBase::isExportFilter() const
229 {
230 return mxImpl->meDirection == FILTERDIRECTION_EXPORT;
231 }
232
233 // ----------------------------------------------------------------------------
234
getArgument(const OUString & rArgName) const235 Any FilterBase::getArgument( const OUString& rArgName ) const
236 {
237 SequenceAsHashMap::const_iterator aIt = mxImpl->maArguments.find( rArgName );
238 return (aIt == mxImpl->maArguments.end()) ? Any() : aIt->second;
239 }
240
getComponentContext() const241 const Reference< XComponentContext >& FilterBase::getComponentContext() const
242 {
243 return mxImpl->mxComponentContext;
244 }
245
getComponentFactory() const246 const Reference< XMultiComponentFactory >& FilterBase::getComponentFactory() const
247 {
248 return mxImpl->mxComponentFactory;
249 }
250
getServiceFactory() const251 const Reference< XMultiServiceFactory >& FilterBase::getServiceFactory() const
252 {
253 return mxImpl->mxServiceFactory;
254 }
255
getModel() const256 const Reference< XModel >& FilterBase::getModel() const
257 {
258 return mxImpl->mxModel;
259 }
260
getModelFactory() const261 const Reference< XMultiServiceFactory >& FilterBase::getModelFactory() const
262 {
263 return mxImpl->mxModelFactory;
264 }
265
getTargetFrame() const266 const Reference< XFrame >& FilterBase::getTargetFrame() const
267 {
268 return mxImpl->mxTargetFrame;
269 }
270
getStatusIndicator() const271 const Reference< XStatusIndicator >& FilterBase::getStatusIndicator() const
272 {
273 return mxImpl->mxStatusIndicator;
274 }
275
getInteractionHandler() const276 const Reference< XInteractionHandler >& FilterBase::getInteractionHandler() const
277 {
278 return mxImpl->mxInteractionHandler;
279 }
280
getMediaDescriptor() const281 MediaDescriptor& FilterBase::getMediaDescriptor() const
282 {
283 return mxImpl->maMediaDesc;
284 }
285
getFileUrl() const286 const OUString& FilterBase::getFileUrl() const
287 {
288 return mxImpl->maFileUrl;
289 }
290
291 namespace {
292
lclIsDosDrive(const OUString & rUrl,sal_Int32 nPos=0)293 inline bool lclIsDosDrive( const OUString& rUrl, sal_Int32 nPos = 0 )
294 {
295 return
296 (rUrl.getLength() >= nPos + 3) &&
297 ((('A' <= rUrl[ nPos ]) && (rUrl[ nPos ] <= 'Z')) || (('a' <= rUrl[ nPos ]) && (rUrl[ nPos ] <= 'z'))) &&
298 (rUrl[ nPos + 1 ] == ':') &&
299 (rUrl[ nPos + 2 ] == '/');
300 }
301
302 } // namespace
303
getAbsoluteUrl(const OUString & rUrl) const304 OUString FilterBase::getAbsoluteUrl( const OUString& rUrl ) const
305 {
306 // handle some special cases before calling ::rtl::Uri::convertRelToAbs()
307
308 const OUString aFileSchema = CREATE_OUSTRING( "file:" );
309 const OUString aFilePrefix = CREATE_OUSTRING( "file:///" );
310 const sal_Int32 nFilePrefixLen = aFilePrefix.getLength();
311 const OUString aUncPrefix = CREATE_OUSTRING( "//" );
312
313 /* (1) convert all backslashes to slashes, and check that passed URL is
314 not empty. */
315 OUString aUrl = rUrl.replace( '\\', '/' );
316 if( aUrl.getLength() == 0 )
317 return aUrl;
318
319 /* (2) add 'file:///' to absolute Windows paths, e.g. convert
320 'C:/path/file' to 'file:///c:/path/file'. */
321 if( lclIsDosDrive( aUrl ) )
322 return aFilePrefix + aUrl;
323
324 /* (3) add 'file:' to UNC paths, e.g. convert '//server/path/file' to
325 'file://server/path/file'. */
326 if( aUrl.match( aUncPrefix ) )
327 return aFileSchema + aUrl;
328
329 /* (4) remove additional slashes from UNC paths, e.g. convert
330 'file://///server/path/file' to 'file://server/path/file'. */
331 if( (aUrl.getLength() >= nFilePrefixLen + 2) &&
332 aUrl.match( aFilePrefix ) &&
333 aUrl.match( aUncPrefix, nFilePrefixLen ) )
334 {
335 return aFileSchema + aUrl.copy( nFilePrefixLen );
336 }
337
338 /* (5) handle URLs relative to current drive, e.g. the URL '/path1/file1'
339 relative to the base URL 'file:///C:/path2/file2' does not result in
340 the expected 'file:///C:/path1/file1', but in 'file:///path1/file1'. */
341 if( (aUrl.getLength() >= 1) && (aUrl[ 0 ] == '/') &&
342 mxImpl->maFileUrl.match( aFilePrefix ) &&
343 lclIsDosDrive( mxImpl->maFileUrl, nFilePrefixLen ) )
344 {
345 return mxImpl->maFileUrl.copy( 0, nFilePrefixLen + 3 ) + aUrl.copy( 1 );
346 }
347
348 try
349 {
350 return ::rtl::Uri::convertRelToAbs( mxImpl->maFileUrl, aUrl );
351 }
352 catch( ::rtl::MalformedUriException& )
353 {
354 }
355 return aUrl;
356 }
357
getStorage() const358 StorageRef FilterBase::getStorage() const
359 {
360 return mxImpl->mxStorage;
361 }
362
openSubStorage(const OUString & rStorageName,bool bCreateMissing) const363 StorageRef FilterBase::openSubStorage( const OUString& rStorageName, bool bCreateMissing ) const
364 {
365 return mxImpl->mxStorage->openSubStorage( rStorageName, bCreateMissing );
366 }
367
openInputStream(const OUString & rStreamName) const368 Reference< XInputStream > FilterBase::openInputStream( const OUString& rStreamName ) const
369 {
370 return mxImpl->mxStorage->openInputStream( rStreamName );
371 }
372
openOutputStream(const OUString & rStreamName) const373 Reference< XOutputStream > FilterBase::openOutputStream( const OUString& rStreamName ) const
374 {
375 return mxImpl->mxStorage->openOutputStream( rStreamName );
376 }
377
commitStorage() const378 void FilterBase::commitStorage() const
379 {
380 mxImpl->mxStorage->commit();
381 }
382
383 // helpers --------------------------------------------------------------------
384
getGraphicHelper() const385 GraphicHelper& FilterBase::getGraphicHelper() const
386 {
387 if( !mxImpl->mxGraphicHelper )
388 mxImpl->mxGraphicHelper.reset( implCreateGraphicHelper() );
389 return *mxImpl->mxGraphicHelper;
390 }
391
getModelObjectHelper() const392 ModelObjectHelper& FilterBase::getModelObjectHelper() const
393 {
394 if( !mxImpl->mxModelObjHelper )
395 mxImpl->mxModelObjHelper.reset( new ModelObjectHelper( mxImpl->mxModelFactory ) );
396 return *mxImpl->mxModelObjHelper;
397 }
398
getOleObjectHelper() const399 OleObjectHelper& FilterBase::getOleObjectHelper() const
400 {
401 if( !mxImpl->mxOleObjHelper )
402 mxImpl->mxOleObjHelper.reset( new OleObjectHelper( mxImpl->mxModelFactory ) );
403 return *mxImpl->mxOleObjHelper;
404 }
405
getVbaProject() const406 VbaProject& FilterBase::getVbaProject() const
407 {
408 if( !mxImpl->mxVbaProject )
409 mxImpl->mxVbaProject.reset( implCreateVbaProject() );
410 return *mxImpl->mxVbaProject;
411 }
412
requestEncryptionData(::comphelper::IDocPasswordVerifier & rVerifier) const413 Sequence< NamedValue > FilterBase::requestEncryptionData( ::comphelper::IDocPasswordVerifier& rVerifier ) const
414 {
415 ::std::vector< OUString > aDefaultPasswords;
416 aDefaultPasswords.push_back( CREATE_OUSTRING( "VelvetSweatshop" ) );
417 return ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
418 rVerifier, mxImpl->maMediaDesc, ::comphelper::DocPasswordRequestType_MS, &aDefaultPasswords );
419 }
420
importBinaryData(StreamDataSequence & orDataSeq,const OUString & rStreamName)421 bool FilterBase::importBinaryData( StreamDataSequence& orDataSeq, const OUString& rStreamName )
422 {
423 OSL_ENSURE( rStreamName.getLength() > 0, "FilterBase::importBinaryData - empty stream name" );
424 if( rStreamName.getLength() == 0 )
425 return false;
426
427 // try to open the stream (this may fail - do not assert)
428 BinaryXInputStream aInStrm( openInputStream( rStreamName ), true );
429 if( aInStrm.isEof() )
430 return false;
431
432 // copy the entire stream to the passed sequence
433 SequenceOutputStream aOutStrm( orDataSeq );
434 aInStrm.copyToStream( aOutStrm );
435 return true;
436 }
437
438 // com.sun.star.lang.XServiceInfo interface -----------------------------------
439
getImplementationName()440 OUString SAL_CALL FilterBase::getImplementationName() throw( RuntimeException )
441 {
442 return implGetImplementationName();
443 }
444
supportsService(const OUString & rServiceName)445 sal_Bool SAL_CALL FilterBase::supportsService( const OUString& rServiceName ) throw( RuntimeException )
446 {
447 return
448 (rServiceName == CREATE_OUSTRING( "com.sun.star.document.ImportFilter" )) ||
449 (rServiceName == CREATE_OUSTRING( "com.sun.star.document.ExportFilter" ));
450 }
451
getSupportedServiceNames()452 Sequence< OUString > SAL_CALL FilterBase::getSupportedServiceNames() throw( RuntimeException )
453 {
454 Sequence< OUString > aServiceNames( 2 );
455 aServiceNames[ 0 ] = CREATE_OUSTRING( "com.sun.star.document.ImportFilter" );
456 aServiceNames[ 1 ] = CREATE_OUSTRING( "com.sun.star.document.ExportFilter" );
457 return aServiceNames;
458 }
459
460 // com.sun.star.lang.XInitialization interface --------------------------------
461
initialize(const Sequence<Any> & rArgs)462 void SAL_CALL FilterBase::initialize( const Sequence< Any >& rArgs ) throw( Exception, RuntimeException )
463 {
464 if( rArgs.getLength() >= 2 ) try
465 {
466 mxImpl->maArguments << rArgs[ 1 ];
467 }
468 catch( Exception& )
469 {
470 }
471 }
472
473 // com.sun.star.document.XImporter interface ----------------------------------
474
setTargetDocument(const Reference<XComponent> & rxDocument)475 void SAL_CALL FilterBase::setTargetDocument( const Reference< XComponent >& rxDocument ) throw( IllegalArgumentException, RuntimeException )
476 {
477 mxImpl->setDocumentModel( rxDocument );
478 mxImpl->meDirection = FILTERDIRECTION_IMPORT;
479 }
480
481 // com.sun.star.document.XExporter interface ----------------------------------
482
setSourceDocument(const Reference<XComponent> & rxDocument)483 void SAL_CALL FilterBase::setSourceDocument( const Reference< XComponent >& rxDocument ) throw( IllegalArgumentException, RuntimeException )
484 {
485 mxImpl->setDocumentModel( rxDocument );
486 mxImpl->meDirection = FILTERDIRECTION_EXPORT;
487 }
488
489 // com.sun.star.document.XFilter interface ------------------------------------
490
filter(const Sequence<PropertyValue> & rMediaDescSeq)491 sal_Bool SAL_CALL FilterBase::filter( const Sequence< PropertyValue >& rMediaDescSeq ) throw( RuntimeException )
492 {
493 if( !mxImpl->mxModel.is() || !mxImpl->mxModelFactory.is() || (mxImpl->meDirection == FILTERDIRECTION_UNKNOWN) )
494 throw RuntimeException();
495
496 sal_Bool bRet = sal_False;
497 setMediaDescriptor( rMediaDescSeq );
498 DocumentOpenedGuard aOpenedGuard( mxImpl->maFileUrl );
499 if( aOpenedGuard.isValid() || !mxImpl->maFileUrl.getLength() )
500 {
501 mxImpl->initializeFilter();
502 switch( mxImpl->meDirection )
503 {
504 case FILTERDIRECTION_UNKNOWN:
505 break;
506 case FILTERDIRECTION_IMPORT:
507 if( mxImpl->mxInStream.is() )
508 {
509 mxImpl->mxStorage = implCreateStorage( mxImpl->mxInStream );
510 bRet = mxImpl->mxStorage.get() && importDocument();
511 }
512 break;
513 case FILTERDIRECTION_EXPORT:
514 if( mxImpl->mxOutStream.is() )
515 {
516 mxImpl->mxStorage = implCreateStorage( mxImpl->mxOutStream );
517 bRet = mxImpl->mxStorage.get() && exportDocument();
518 }
519 break;
520 }
521 mxImpl->finalizeFilter();
522 }
523 return bRet;
524 }
525
cancel()526 void SAL_CALL FilterBase::cancel() throw( RuntimeException )
527 {
528 }
529
530 // protected ------------------------------------------------------------------
531
implGetInputStream(MediaDescriptor & rMediaDesc) const532 Reference< XInputStream > FilterBase::implGetInputStream( MediaDescriptor& rMediaDesc ) const
533 {
534 return rMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_INPUTSTREAM(), Reference< XInputStream >() );
535 }
536
implGetOutputStream(MediaDescriptor & rMediaDesc) const537 Reference< XStream > FilterBase::implGetOutputStream( MediaDescriptor& rMediaDesc ) const
538 {
539 return rMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_STREAMFOROUTPUT(), Reference< XStream >() );
540 }
541
542 // private --------------------------------------------------------------------
543
setMediaDescriptor(const Sequence<PropertyValue> & rMediaDescSeq)544 void FilterBase::setMediaDescriptor( const Sequence< PropertyValue >& rMediaDescSeq )
545 {
546 mxImpl->maMediaDesc << rMediaDescSeq;
547
548 switch( mxImpl->meDirection )
549 {
550 case FILTERDIRECTION_UNKNOWN:
551 OSL_ENSURE( false, "FilterBase::setMediaDescriptor - invalid filter direction" );
552 break;
553 case FILTERDIRECTION_IMPORT:
554 mxImpl->maMediaDesc.addInputStream();
555 mxImpl->mxInStream = implGetInputStream( mxImpl->maMediaDesc );
556 OSL_ENSURE( mxImpl->mxInStream.is(), "FilterBase::setMediaDescriptor - missing input stream" );
557 break;
558 case FILTERDIRECTION_EXPORT:
559 mxImpl->mxOutStream = implGetOutputStream( mxImpl->maMediaDesc );
560 OSL_ENSURE( mxImpl->mxOutStream.is(), "FilterBase::setMediaDescriptor - missing output stream" );
561 break;
562 }
563
564 mxImpl->maFileUrl = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_URL(), OUString() );
565 mxImpl->mxTargetFrame = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_FRAME(), Reference< XFrame >() );
566 mxImpl->mxStatusIndicator = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_STATUSINDICATOR(), Reference< XStatusIndicator >() );
567 mxImpl->mxInteractionHandler = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_INTERACTIONHANDLER(), Reference< XInteractionHandler >() );
568 }
569
implCreateGraphicHelper() const570 GraphicHelper* FilterBase::implCreateGraphicHelper() const
571 {
572 // default: return base implementation without any special behaviour
573 return new GraphicHelper( mxImpl->mxComponentContext, mxImpl->mxTargetFrame, mxImpl->mxStorage );
574 }
575
576 // ============================================================================
577
578 } // namespace core
579 } // namespace oox
580