xref: /aoo42x/main/ucb/source/ucp/tdoc/tdoc_storage.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_ucb.hxx"
30 
31 /**************************************************************************
32 								TODO
33  **************************************************************************
34 
35  - remove root storage access workaround
36 
37  *************************************************************************/
38 
39 #define ROOTSTORAGE_ACCESS_WORKAROUND 1
40 
41 #include <memory>
42 
43 #include "com/sun/star/beans/XPropertySet.hpp"
44 #include "com/sun/star/embed/ElementModes.hpp"
45 #include "com/sun/star/lang/XSingleServiceFactory.hpp"
46 
47 #include "tdoc_uri.hxx"
48 #include "tdoc_docmgr.hxx"
49 #include "tdoc_stgelems.hxx"
50 
51 #include "tdoc_storage.hxx"
52 
53 using namespace com::sun::star;
54 using namespace tdoc_ucp;
55 
56 
57 //=========================================================================
58 //=========================================================================
59 //
60 // StorageElementFactory Implementation.
61 //
62 //=========================================================================
63 //=========================================================================
64 
65 StorageElementFactory::StorageElementFactory(
66     const uno::Reference< lang::XMultiServiceFactory > & xSMgr,
67     const rtl::Reference< OfficeDocumentsManager > & xDocsMgr )
68 : m_xDocsMgr( xDocsMgr ),
69   m_xSMgr( xSMgr )
70 {
71 }
72 
73 //=========================================================================
74 StorageElementFactory::~StorageElementFactory()
75 {
76     OSL_ENSURE( m_aMap.size() == 0,
77         "StorageElementFactory::~StorageElementFactory - Dangling storages!" );
78 }
79 
80 //=========================================================================
81 uno::Reference< embed::XStorage >
82 StorageElementFactory::createTemporaryStorage()
83 	throw ( uno::Exception,
84 			uno::RuntimeException )
85 {
86 	uno::Reference< embed::XStorage > xStorage;
87 	uno::Reference< lang::XSingleServiceFactory > xStorageFac;
88 	if ( m_xSMgr.is() )
89 	{
90 		xStorageFac = uno::Reference< lang::XSingleServiceFactory >(
91    			m_xSMgr->createInstance(
92    				rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
93    					"com.sun.star.embed.StorageFactory" ) ) ),
94    			uno::UNO_QUERY );
95 	}
96 
97 	OSL_ENSURE( xStorageFac.is(), "Can't create storage factory!" );
98 	if ( xStorageFac.is() )
99 		xStorage = uno::Reference< embed::XStorage >(
100 							xStorageFac->createInstance(),
101 							uno::UNO_QUERY );
102 
103 	if ( !xStorage.is() )
104 		throw uno::RuntimeException();
105 
106 	return xStorage;
107 }
108 
109 //=========================================================================
110 uno::Reference< embed::XStorage >
111 StorageElementFactory::createStorage( const rtl::OUString & rUri,
112                                       StorageAccessMode eMode )
113     throw ( embed::InvalidStorageException,
114             lang::IllegalArgumentException,
115             io::IOException,
116             embed::StorageWrappedTargetException,
117             uno::RuntimeException )
118 {
119     osl::MutexGuard aGuard( m_aMutex );
120 
121     if ( ( eMode != READ ) &&
122          ( eMode != READ_WRITE_NOCREATE ) &&
123          ( eMode != READ_WRITE_CREATE ) )
124         throw lang::IllegalArgumentException(
125             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
126                 "Invalid open mode!" ) ),
127             uno::Reference< uno::XInterface >(),
128             sal_Int16( 2 ) );
129 
130     Uri aUri( rUri );
131     if ( aUri.isRoot() )
132     {
133         throw lang::IllegalArgumentException(
134             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
135                 "Root never has a storage!" ) ),
136             uno::Reference< uno::XInterface >(),
137             sal_Int16( 1 ) );
138     }
139 
140     rtl::OUString aUriKey
141         ( ( rUri.getStr()[ rUri.getLength() - 1 ] == sal_Unicode( '/' ) )
142           ? rUri.copy( 0, rUri.getLength() - 1 )
143           : rUri );
144 
145     StorageMap::iterator aIt ( m_aMap.begin() );
146     StorageMap::iterator aEnd( m_aMap.end() );
147 
148     while ( aIt != aEnd )
149     {
150         if ( (*aIt).first.first == aUriKey )
151         {
152             // URI matches. Now, check open mode.
153             bool bMatch = true;
154             switch ( eMode )
155             {
156                 case READ:
157                     // No need to check; storage is at least readable.
158                     bMatch = true;
159                     break;
160 
161                 case READ_WRITE_NOCREATE:
162                 case READ_WRITE_CREATE:
163                     // If found storage is writable, it can be used.
164                     // If not, a new one must be created.
165                     bMatch = (*aIt).first.second;
166                     break;
167             }
168 
169             if ( bMatch )
170                 break;
171         }
172         ++aIt;
173     }
174 
175     if ( aIt == aEnd )
176     {
177         uno::Reference< embed::XStorage > xParentStorage;
178 
179         // documents never have a parent storage.
180         if ( !aUri.isDocument() )
181         {
182             xParentStorage = queryParentStorage( aUriKey, eMode );
183 
184             if ( !xParentStorage.is() )
185             {
186                 // requested to create new storage, but failed?
187                 OSL_ENSURE( eMode != READ_WRITE_CREATE,
188                             "Unable to create parent storage!" );
189                 return xParentStorage;
190             }
191         }
192 
193         uno::Reference< embed::XStorage > xStorage
194             = queryStorage( xParentStorage, aUriKey, eMode );
195 
196         if ( !xStorage.is() )
197         {
198             // requested to create new storage, but failed?
199             OSL_ENSURE( eMode != READ_WRITE_CREATE,
200                         "Unable to create storage!" );
201             return xStorage;
202         }
203 
204         bool bWritable = ( ( eMode == READ_WRITE_NOCREATE )
205                             || ( eMode == READ_WRITE_CREATE ) );
206 
207         std::auto_ptr< Storage > xElement(
208             new Storage( m_xSMgr, this, aUriKey, xParentStorage, xStorage ) );
209 
210         aIt = m_aMap.insert(
211             StorageMap::value_type(
212                 std::pair< rtl::OUString, bool >( aUriKey, bWritable ),
213                 xElement.get() ) ).first;
214 
215         aIt->second->m_aContainerIt = aIt;
216         xElement.release();
217         return aIt->second;
218     }
219     else if ( osl_incrementInterlockedCount( &aIt->second->m_refCount ) > 1 )
220     {
221         rtl::Reference< Storage > xElement( aIt->second );
222         osl_decrementInterlockedCount( &aIt->second->m_refCount );
223         return aIt->second;
224     }
225     else
226     {
227         osl_decrementInterlockedCount( &aIt->second->m_refCount );
228         aIt->second->m_aContainerIt = m_aMap.end();
229 
230         uno::Reference< embed::XStorage > xParentStorage;
231 
232         // documents never have a parent storage.
233         if ( !aUri.isDocument() )
234         {
235             xParentStorage = queryParentStorage( aUriKey, eMode );
236 
237             if ( !xParentStorage.is() )
238             {
239                 // requested to create new storage, but failed?
240                 OSL_ENSURE( eMode != READ_WRITE_CREATE,
241                             "Unable to create parent storage!" );
242                 return xParentStorage;
243             }
244         }
245 
246         uno::Reference< embed::XStorage > xStorage
247             = queryStorage( xParentStorage, aUriKey, eMode );
248 
249         if ( !xStorage.is() )
250         {
251             // requested to create new storage, but failed?
252             OSL_ENSURE( eMode != READ_WRITE_CREATE,
253                         "Unable to create storage!" );
254             return xStorage;
255         }
256 
257         aIt->second
258             = new Storage( m_xSMgr, this, aUriKey, xParentStorage, xStorage );
259         aIt->second->m_aContainerIt = aIt;
260         return aIt->second;
261     }
262 }
263 
264 //=========================================================================
265 uno::Reference< io::XInputStream >
266 StorageElementFactory::createInputStream( const rtl::OUString & rUri,
267                                           const rtl::OUString & rPassword )
268     throw ( embed::InvalidStorageException,
269             lang::IllegalArgumentException,
270             io::IOException,
271             embed::StorageWrappedTargetException,
272             packages::WrongPasswordException,
273             uno::RuntimeException )
274 {
275     osl::MutexGuard aGuard( m_aMutex );
276 
277     uno::Reference< embed::XStorage > xParentStorage
278         = queryParentStorage( rUri, READ );
279 
280     // Each stream must have a parent storage.
281     if ( !xParentStorage.is() )
282         return uno::Reference< io::XInputStream >();
283 
284     uno::Reference< io::XStream > xStream
285         = queryStream( xParentStorage, rUri, rPassword, READ, false );
286 
287     if ( !xStream.is() )
288         return uno::Reference< io::XInputStream >();
289 
290     return xStream->getInputStream();
291 }
292 
293 //=========================================================================
294 uno::Reference< io::XOutputStream >
295 StorageElementFactory::createOutputStream( const rtl::OUString & rUri,
296                                            const rtl::OUString & rPassword,
297                                            bool bTruncate )
298     throw ( embed::InvalidStorageException,
299             lang::IllegalArgumentException,
300             io::IOException,
301             embed::StorageWrappedTargetException,
302             packages::WrongPasswordException,
303             uno::RuntimeException )
304 {
305     osl::MutexGuard aGuard( m_aMutex );
306 
307     uno::Reference< embed::XStorage > xParentStorage
308         = queryParentStorage( rUri, READ_WRITE_CREATE );
309 
310     // Each stream must have a parent storage.
311     if ( !xParentStorage.is() )
312     {
313         OSL_ENSURE( false,
314                     "StorageElementFactory::createOutputStream - "
315                     "Unable to create parent storage!" );
316         return uno::Reference< io::XOutputStream >();
317     }
318 
319     uno::Reference< io::XStream > xStream
320         = queryStream(
321             xParentStorage, rUri, rPassword, READ_WRITE_CREATE, bTruncate );
322 
323     if ( !xStream.is() )
324     {
325         OSL_ENSURE( false,
326                     "StorageElementFactory::createOutputStream - "
327                     "Unable to create stream!" );
328         return uno::Reference< io::XOutputStream >();
329     }
330 
331     // Note: We need a wrapper to hold a reference to the parent storage to
332     //       ensure that nobody else owns it at the moment we want to commit
333     //       our changes. (There can be only one writable instance at a time
334     //       and even no writable instance if there is  already another
335     //       read-only instance!)
336     return uno::Reference< io::XOutputStream >(
337         new OutputStream(
338             m_xSMgr, rUri, xParentStorage, xStream->getOutputStream() ) );
339 }
340 
341 //=========================================================================
342 uno::Reference< io::XStream >
343 StorageElementFactory::createStream( const rtl::OUString & rUri,
344                                      const rtl::OUString & rPassword,
345                                      bool bTruncate )
346     throw ( embed::InvalidStorageException,
347             lang::IllegalArgumentException,
348             io::IOException,
349             embed::StorageWrappedTargetException,
350             packages::WrongPasswordException,
351             uno::RuntimeException )
352 {
353     osl::MutexGuard aGuard( m_aMutex );
354 
355     uno::Reference< embed::XStorage > xParentStorage
356         = queryParentStorage( rUri, READ_WRITE_CREATE );
357 
358     // Each stream must have a parent storage.
359     if ( !xParentStorage.is() )
360     {
361         OSL_ENSURE( false,
362                     "StorageElementFactory::createStream - "
363                     "Unable to create parent storage!" );
364         return uno::Reference< io::XStream >();
365     }
366 
367     uno::Reference< io::XStream > xStream
368         = queryStream(
369             xParentStorage, rUri, rPassword, READ_WRITE_NOCREATE, bTruncate );
370 
371     if ( !xStream.is() )
372     {
373         OSL_ENSURE( false,
374                     "StorageElementFactory::createStream - "
375                     "Unable to create stream!" );
376         return uno::Reference< io::XStream >();
377     }
378 
379     return uno::Reference< io::XStream >(
380         new Stream( m_xSMgr, rUri, xParentStorage, xStream ) );
381 }
382 
383 //=========================================================================
384 void StorageElementFactory::releaseElement( Storage * pElement ) SAL_THROW( () )
385 {
386     OSL_ASSERT( pElement );
387     osl::MutexGuard aGuard( m_aMutex );
388     if ( pElement->m_aContainerIt != m_aMap.end() )
389         m_aMap.erase( pElement->m_aContainerIt );
390 }
391 
392 //=========================================================================
393 //
394 // Non-UNO interface
395 //
396 //=========================================================================
397 
398 uno::Reference< embed::XStorage > StorageElementFactory::queryParentStorage(
399         const rtl::OUString & rUri, StorageAccessMode eMode )
400     throw ( embed::InvalidStorageException,
401             lang::IllegalArgumentException,
402             io::IOException,
403             embed::StorageWrappedTargetException,
404             uno::RuntimeException )
405 {
406     uno::Reference< embed::XStorage > xParentStorage;
407 
408     Uri aUri( rUri );
409     Uri aParentUri( aUri.getParentUri() );
410     if ( !aParentUri.isRoot() )
411     {
412         xParentStorage = createStorage( aUri.getParentUri(), eMode );
413         OSL_ENSURE( xParentStorage.is()
414                     // requested to create new storage, but failed?
415                     || ( eMode != READ_WRITE_CREATE ),
416                     "StorageElementFactory::queryParentStorage - No storage!" );
417     }
418     return xParentStorage;
419 }
420 
421 //=========================================================================
422 uno::Reference< embed::XStorage > StorageElementFactory::queryStorage(
423         const uno::Reference< embed::XStorage > & xParentStorage,
424         const rtl::OUString & rUri,
425         StorageAccessMode eMode )
426     throw ( embed::InvalidStorageException,
427             lang::IllegalArgumentException,
428             io::IOException,
429             embed::StorageWrappedTargetException,
430             uno::RuntimeException )
431 {
432     uno::Reference< embed::XStorage > xStorage;
433 
434     Uri aUri( rUri );
435 
436     if ( !xParentStorage.is() )
437     {
438         // document storage
439 
440         xStorage = m_xDocsMgr->queryStorage( aUri.getDocumentId() );
441 
442         if ( !xStorage.is() )
443         {
444             if ( eMode == READ_WRITE_CREATE )
445                 throw lang::IllegalArgumentException(
446                     rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
447                         "Invalid open mode: document storages cannot be "
448                         "created!" ) ),
449                     uno::Reference< uno::XInterface >(),
450                     sal_Int16( 2 ) );
451             else
452                 throw embed::InvalidStorageException(
453                     rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
454                         "Invalid document id!" ) ),
455                     uno::Reference< uno::XInterface >() );
456         }
457 
458         // match xStorage's open mode against requested open mode
459 
460         uno::Reference< beans::XPropertySet > xPropSet(
461             xStorage, uno::UNO_QUERY );
462         OSL_ENSURE( xPropSet.is(),
463                     "StorageElementFactory::queryStorage - "
464                     "No XPropertySet interface!" );
465         try
466         {
467             uno::Any aPropValue = xPropSet->getPropertyValue(
468                 rtl::OUString(
469                     RTL_CONSTASCII_USTRINGPARAM( "OpenMode" ) ) );
470 
471             sal_Int32 nOpenMode = 0;
472             if ( aPropValue >>= nOpenMode )
473             {
474                 switch ( eMode )
475                 {
476                     case READ:
477                         if ( !( nOpenMode & embed::ElementModes::READ ) )
478                         {
479                             // document opened, but not readable.
480                             throw embed::InvalidStorageException(
481                                 rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
482                                     "Storage is open, but not readable!" ) ),
483                                 uno::Reference< uno::XInterface >() );
484                         }
485                         // storage okay
486                         break;
487 
488                     case READ_WRITE_NOCREATE:
489                     case READ_WRITE_CREATE:
490                         if ( !( nOpenMode & embed::ElementModes::WRITE ) )
491                         {
492                             // document opened, but not writable.
493                             throw embed::InvalidStorageException(
494                                 rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
495                                     "Storage is open, but not writable!" ) ),
496                                 uno::Reference< uno::XInterface >() );
497                         }
498                         // storage okay
499                         break;
500                 }
501             }
502             else
503             {
504                 OSL_ENSURE(
505                     false, "Bug! Value of property OpenMode has wrong type!" );
506 
507                 throw uno::RuntimeException(
508                     rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
509                         "Bug! Value of property OpenMode has wrong type!" ) ),
510                     uno::Reference< uno::XInterface >() );
511             }
512         }
513         catch ( beans::UnknownPropertyException const & e )
514         {
515             OSL_ENSURE( false, "Property OpenMode not supported!" );
516 
517             throw embed::StorageWrappedTargetException(
518                     rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
519                         "Bug! Value of property OpenMode has wrong type!" ) ),
520                     uno::Reference< uno::XInterface >(),
521                     uno::makeAny( e ) );
522         }
523         catch ( lang::WrappedTargetException const & e )
524         {
525             OSL_ENSURE( false, "Caught WrappedTargetException!" );
526 
527             throw embed::StorageWrappedTargetException(
528                     rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
529                         "WrappedTargetException during getPropertyValue!" ) ),
530                     uno::Reference< uno::XInterface >(),
531                     uno::makeAny( e ) );
532         }
533     }
534     else
535     {
536         // sub storage
537 
538         const rtl::OUString & rName = aUri.getDecodedName();
539 
540         if ( eMode == READ )
541         {
542             try
543             {
544                 sal_Int32 nOpenMode = embed::ElementModes::READ
545                                       | embed::ElementModes::NOCREATE;
546                 xStorage
547                     = xParentStorage->openStorageElement( rName, nOpenMode );
548             }
549             catch ( io::IOException const & )
550             {
551                 // Another chance: Try to clone storage.
552                 xStorage = createTemporaryStorage();
553                 xParentStorage->copyStorageElementLastCommitTo( rName,
554                                                                 xStorage );
555             }
556         }
557         else
558         {
559             sal_Int32 nOpenMode = embed::ElementModes::READWRITE;
560             if ( eMode == READ_WRITE_NOCREATE )
561                 nOpenMode |= embed::ElementModes::NOCREATE;
562 
563             xStorage = xParentStorage->openStorageElement( rName, nOpenMode );
564         }
565     }
566 
567     OSL_ENSURE( xStorage.is() || ( eMode != READ_WRITE_CREATE ),
568                 "StorageElementFactory::queryStorage - No storage!" );
569     return xStorage;
570 }
571 
572 //=========================================================================
573 uno::Reference< io::XStream >
574 StorageElementFactory::queryStream(
575                 const uno::Reference< embed::XStorage > & xParentStorage,
576                 const rtl::OUString & rUri,
577                 const rtl::OUString & rPassword,
578                 StorageAccessMode eMode,
579                 bool bTruncate )
580     throw ( embed::InvalidStorageException,
581             lang::IllegalArgumentException,
582             io::IOException,
583             embed::StorageWrappedTargetException,
584             packages::WrongPasswordException,
585             uno::RuntimeException )
586 {
587     osl::MutexGuard aGuard( m_aMutex );
588 
589     if ( !xParentStorage.is() )
590     {
591         throw lang::IllegalArgumentException(
592             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
593                 "No parent storage!" ) ),
594             uno::Reference< uno::XInterface >(),
595             sal_Int16( 2 ) );
596     }
597 
598     Uri aUri( rUri );
599     if ( aUri.isRoot() )
600     {
601         throw lang::IllegalArgumentException(
602             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
603                 "Root never is a stream!" ) ),
604             uno::Reference< uno::XInterface >(),
605             sal_Int16( 2 ) );
606     }
607     else if ( aUri.isDocument() )
608     {
609         throw lang::IllegalArgumentException(
610             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
611                 "A document never is a stream!" ) ),
612             uno::Reference< uno::XInterface >(),
613             sal_Int16( 2 ) );
614     }
615 
616     sal_Int32 nOpenMode;
617     switch ( eMode )
618     {
619         case READ:
620             nOpenMode = embed::ElementModes::READ
621                         | embed::ElementModes::NOCREATE
622                         | embed::ElementModes::SEEKABLE;
623             break;
624 
625         case READ_WRITE_NOCREATE:
626             nOpenMode = embed::ElementModes::READWRITE
627                         | embed::ElementModes::NOCREATE
628                         | embed::ElementModes::SEEKABLE;
629 
630             if ( bTruncate )
631                 nOpenMode |= embed::ElementModes::TRUNCATE;
632 
633             break;
634 
635         case READ_WRITE_CREATE:
636             nOpenMode = embed::ElementModes::READWRITE
637                         | embed::ElementModes::SEEKABLE;
638 
639             if ( bTruncate )
640                 nOpenMode |= embed::ElementModes::TRUNCATE;
641 
642             break;
643 
644         default:
645             OSL_ENSURE( false,
646                 "StorageElementFactory::queryStream : Unknown open mode!" );
647 
648             throw embed::InvalidStorageException(
649                 rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
650                     "Unknown open mode!" ) ),
651                 uno::Reference< uno::XInterface >() );
652     }
653 
654     // No object re-usage mechanism; streams are seekable => not stateless.
655 
656     uno::Reference< io::XStream > xStream;
657     if ( rPassword.getLength() > 0 )
658     {
659         if ( eMode == READ )
660         {
661             try
662             {
663                 xStream = xParentStorage->cloneEncryptedStreamElement(
664                                                          aUri.getDecodedName(),
665                                                          rPassword );
666             }
667             catch ( packages::NoEncryptionException const & )
668             {
669                 xStream
670                     = xParentStorage->cloneStreamElement( aUri.getDecodedName() );
671             }
672         }
673         else
674         {
675             try
676             {
677                 xStream = xParentStorage->openEncryptedStreamElement(
678                                                          aUri.getDecodedName(),
679                                                          nOpenMode,
680                                                          rPassword );
681             }
682             catch ( packages::NoEncryptionException const & )
683             {
684                 xStream
685                     = xParentStorage->openStreamElement( aUri.getDecodedName(),
686                                                          nOpenMode );
687             }
688         }
689     }
690     else
691     {
692         if ( eMode == READ )
693         {
694             xStream = xParentStorage->cloneStreamElement( aUri.getDecodedName() );
695         }
696         else
697         {
698             xStream = xParentStorage->openStreamElement( aUri.getDecodedName(),
699                                                          nOpenMode );
700         }
701     }
702 
703     if ( !xStream.is() )
704     {
705         throw embed::InvalidStorageException(
706             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
707                 "No stream!" ) ),
708             uno::Reference< uno::XInterface >() );
709     }
710 
711     return xStream;
712 }
713