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