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