LibreOffice Module ucb (master) 1
tdoc_storage.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <com/sun/star/beans/XPropertySet.hpp>
21#include <com/sun/star/embed/ElementModes.hpp>
22#include <com/sun/star/embed/InvalidStorageException.hpp>
23#include <com/sun/star/embed/StorageFactory.hpp>
24#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
25#include <com/sun/star/io/IOException.hpp>
26#include <com/sun/star/packages/NoEncryptionException.hpp>
28#include <utility>
29#include <osl/diagnose.h>
30
31#include "tdoc_uri.hxx"
32#include "tdoc_docmgr.hxx"
33#include "tdoc_stgelems.hxx"
34
35#include "tdoc_storage.hxx"
36
37using namespace com::sun::star;
38using namespace tdoc_ucp;
39
40
41// StorageElementFactory Implementation.
42
43
44StorageElementFactory::StorageElementFactory(
45 uno::Reference< uno::XComponentContext > xContext,
47: m_xDocsMgr(std::move( xDocsMgr )),
48 m_xContext(std::move( xContext ))
49{
50}
51
52
54{
55 OSL_ENSURE( m_aMap.empty(),
56 "StorageElementFactory::~StorageElementFactory - Dangling storages!" );
57}
58
59
60uno::Reference< embed::XStorage >
62{
63 uno::Reference< embed::XStorage > xStorage;
64 uno::Reference< lang::XSingleServiceFactory > xStorageFac;
65 if ( m_xContext.is() )
66 {
67 xStorageFac = embed::StorageFactory::create( m_xContext );
68 }
69
70 OSL_ENSURE( xStorageFac.is(), "Can't create storage factory!" );
71 if ( xStorageFac.is() )
72 xStorage.set( xStorageFac->createInstance(), uno::UNO_QUERY );
73
74 if ( !xStorage.is() )
75 throw uno::RuntimeException();
76
77 return xStorage;
78}
79
80
81uno::Reference< embed::XStorage >
83 StorageAccessMode eMode )
84{
85 osl::MutexGuard aGuard( m_aMutex );
86
87 if ( ( eMode != READ ) &&
90 throw lang::IllegalArgumentException(
91 "Invalid open mode!",
92 uno::Reference< uno::XInterface >(),
93 sal_Int16( 2 ) );
94
95 Uri aUri( rUri );
96 if ( aUri.isRoot() )
97 {
98 throw lang::IllegalArgumentException(
99 "Root never has a storage!",
100 uno::Reference< uno::XInterface >(),
101 sal_Int16( 1 ) );
102 }
103
104 OUString aUriKey
105 ( rUri.endsWith("/")
106 ? rUri.copy( 0, rUri.getLength() - 1 )
107 : rUri );
108
109 StorageMap::iterator aIt ( m_aMap.begin() );
110 StorageMap::iterator aEnd( m_aMap.end() );
111
112 while ( aIt != aEnd )
113 {
114 if ( (*aIt).first.first == aUriKey )
115 {
116 // URI matches. Now, check open mode.
117 bool bMatch = true;
118 switch ( eMode )
119 {
120 case READ:
121 // No need to check; storage is at least readable.
122 bMatch = true;
123 break;
124
127 // If found storage is writable, it can be used.
128 // If not, a new one must be created.
129 bMatch = (*aIt).first.second;
130 break;
131 }
132
133 if ( bMatch )
134 break;
135 }
136 ++aIt;
137 }
138
139 if ( aIt == aEnd )
140 {
141 uno::Reference< embed::XStorage > xParentStorage;
142
143 // documents never have a parent storage.
144 if ( !aUri.isDocument() )
145 {
146 xParentStorage = queryParentStorage( aUriKey, eMode );
147
148 if ( !xParentStorage.is() )
149 {
150 // requested to create new storage, but failed?
151 OSL_ENSURE( eMode != READ_WRITE_CREATE,
152 "Unable to create parent storage!" );
153 return xParentStorage;
154 }
155 }
156
157 uno::Reference< embed::XStorage > xStorage
158 = queryStorage( xParentStorage, aUriKey, eMode );
159
160 if ( !xStorage.is() )
161 {
162 // requested to create new storage, but failed?
163 OSL_ENSURE( eMode != READ_WRITE_CREATE,
164 "Unable to create storage!" );
165 return xStorage;
166 }
167
168 bool bWritable = ( ( eMode == READ_WRITE_NOCREATE )
169 || ( eMode == READ_WRITE_CREATE ) );
170
172 new Storage( m_xContext, this, aUriKey, xParentStorage, xStorage ) );
173
174 aIt = m_aMap.emplace(
175 std::pair< OUString, bool >( aUriKey, bWritable ),
176 xElement.get() ).first;
177
178 aIt->second->m_aContainerIt = aIt;
179 return aIt->second;
180 }
181 else if ( osl_atomic_increment( &aIt->second->m_refCount ) > 1 )
182 {
183 uno::Reference< embed::XStorage > xElement( aIt->second );
184 osl_atomic_decrement( &aIt->second->m_refCount );
185 return xElement;
186 }
187 else
188 {
189 osl_atomic_decrement( &aIt->second->m_refCount );
190 aIt->second->m_aContainerIt = m_aMap.end();
191
192 uno::Reference< embed::XStorage > xParentStorage;
193
194 // documents never have a parent storage.
195 if ( !aUri.isDocument() )
196 {
197 xParentStorage = queryParentStorage( aUriKey, eMode );
198
199 if ( !xParentStorage.is() )
200 {
201 // requested to create new storage, but failed?
202 OSL_ENSURE( eMode != READ_WRITE_CREATE,
203 "Unable to create parent storage!" );
204 return xParentStorage;
205 }
206 }
207
208 uno::Reference< embed::XStorage > xStorage
209 = queryStorage( xParentStorage, aUriKey, eMode );
210
211 if ( !xStorage.is() )
212 {
213 // requested to create new storage, but failed?
214 OSL_ENSURE( eMode != READ_WRITE_CREATE,
215 "Unable to create storage!" );
216 return xStorage;
217 }
218
219 rtl::Reference<Storage> pNewStorage = new Storage( m_xContext, this, aUriKey, xParentStorage, xStorage );
220 aIt->second = pNewStorage.get();
221 aIt->second->m_aContainerIt = aIt;
222 return pNewStorage;
223 }
224}
225
226
227uno::Reference< io::XInputStream >
229 const OUString & rPassword )
230{
231 osl::MutexGuard aGuard( m_aMutex );
232
233 uno::Reference< embed::XStorage > xParentStorage
234 = queryParentStorage( rUri, READ );
235
236 // Each stream must have a parent storage.
237 if ( !xParentStorage.is() )
238 return uno::Reference< io::XInputStream >();
239
240 uno::Reference< io::XStream > xStream
241 = queryStream( xParentStorage, rUri, rPassword, READ, false );
242
243 if ( !xStream.is() )
244 return uno::Reference< io::XInputStream >();
245
246 return xStream->getInputStream();
247}
248
249
250uno::Reference< io::XOutputStream >
252 const OUString & rPassword,
253 bool bTruncate )
254{
255 osl::MutexGuard aGuard( m_aMutex );
256
257 uno::Reference< embed::XStorage > xParentStorage
259
260 // Each stream must have a parent storage.
261 if ( !xParentStorage.is() )
262 {
263 OSL_FAIL( "StorageElementFactory::createOutputStream - "
264 "Unable to create parent storage!" );
265 return uno::Reference< io::XOutputStream >();
266 }
267
268 uno::Reference< io::XStream > xStream
269 = queryStream(
270 xParentStorage, rUri, rPassword, READ_WRITE_CREATE, bTruncate );
271
272 if ( !xStream.is() )
273 {
274 OSL_FAIL( "StorageElementFactory::createOutputStream - "
275 "Unable to create stream!" );
276 return uno::Reference< io::XOutputStream >();
277 }
278
279 // Note: We need a wrapper to hold a reference to the parent storage to
280 // ensure that nobody else owns it at the moment we want to commit
281 // our changes. (There can be only one writable instance at a time
282 // and even no writable instance if there is already another
283 // read-only instance!)
284 return uno::Reference< io::XOutputStream >(
285 new OutputStream( m_xContext, rUri, xParentStorage, xStream->getOutputStream() ) );
286}
287
288
289uno::Reference< io::XStream >
291 const OUString & rPassword,
292 bool bTruncate )
293{
294 osl::MutexGuard aGuard( m_aMutex );
295
296 uno::Reference< embed::XStorage > xParentStorage
298
299 // Each stream must have a parent storage.
300 if ( !xParentStorage.is() )
301 {
302 OSL_FAIL( "StorageElementFactory::createStream - "
303 "Unable to create parent storage!" );
304 return uno::Reference< io::XStream >();
305 }
306
307 uno::Reference< io::XStream > xStream
308 = queryStream(
309 xParentStorage, rUri, rPassword, READ_WRITE_NOCREATE, bTruncate );
310
311 if ( !xStream.is() )
312 {
313 OSL_FAIL( "StorageElementFactory::createStream - "
314 "Unable to create stream!" );
315 return uno::Reference< io::XStream >();
316 }
317
318 return uno::Reference< io::XStream >(
319 new Stream( m_xContext, m_xDocsMgr, rUri, xParentStorage, xStream ) );
320}
321
322
324{
325 OSL_ASSERT( pElement );
326 osl::MutexGuard aGuard( m_aMutex );
327 if ( pElement->m_aContainerIt != m_aMap.end() )
328 m_aMap.erase( pElement->m_aContainerIt );
329}
330
331
332// Non-UNO interface
333
334
335uno::Reference< embed::XStorage > StorageElementFactory::queryParentStorage(
336 const OUString & rUri, StorageAccessMode eMode )
337{
338 uno::Reference< embed::XStorage > xParentStorage;
339
340 Uri aUri( rUri );
341 Uri aParentUri( aUri.getParentUri() );
342 if ( !aParentUri.isRoot() )
343 {
344 xParentStorage = createStorage( aUri.getParentUri(), eMode );
345 OSL_ENSURE( xParentStorage.is()
346 // requested to create new storage, but failed?
347 || ( eMode != READ_WRITE_CREATE ),
348 "StorageElementFactory::queryParentStorage - No storage!" );
349 }
350 return xParentStorage;
351}
352
353
354uno::Reference< embed::XStorage > StorageElementFactory::queryStorage(
355 const uno::Reference< embed::XStorage > & xParentStorage,
356 const OUString & rUri,
357 StorageAccessMode eMode )
358{
359 uno::Reference< embed::XStorage > xStorage;
360
361 Uri aUri( rUri );
362
363 if ( !xParentStorage.is() )
364 {
365 // document storage
366
367 xStorage = m_xDocsMgr->queryStorage( aUri.getDocumentId() );
368
369 if ( !xStorage.is() )
370 {
371 if ( eMode == READ_WRITE_CREATE )
372 throw lang::IllegalArgumentException(
373 "Invalid open mode: document storages cannot be created!",
374 uno::Reference< uno::XInterface >(),
375 sal_Int16( 2 ) );
376 else
377 throw embed::InvalidStorageException(
378 "Invalid document id!",
379 uno::Reference< uno::XInterface >() );
380 }
381
382 // match xStorage's open mode against requested open mode
383
384 uno::Reference< beans::XPropertySet > xPropSet(
385 xStorage, uno::UNO_QUERY );
386 OSL_ENSURE( xPropSet.is(),
387 "StorageElementFactory::queryStorage - "
388 "No XPropertySet interface!" );
389 try
390 {
391 uno::Any aPropValue = xPropSet->getPropertyValue("OpenMode");
392
393 sal_Int32 nOpenMode = 0;
394 if ( aPropValue >>= nOpenMode )
395 {
396 switch ( eMode )
397 {
398 case READ:
399 if ( !( nOpenMode & embed::ElementModes::READ ) )
400 {
401 // document opened, but not readable.
402 throw embed::InvalidStorageException(
403 "Storage is open, but not readable!" );
404 }
405 // storage okay
406 break;
407
410 if ( !( nOpenMode & embed::ElementModes::WRITE ) )
411 {
412 // document opened, but not writable.
413 throw embed::InvalidStorageException(
414 "Storage is open, but not writable!" );
415 }
416 // storage okay
417 break;
418 }
419 }
420 else
421 {
422 OSL_FAIL(
423 "Bug! Value of property OpenMode has wrong type!" );
424
425 throw uno::RuntimeException(
426 "Bug! Value of property OpenMode has wrong type!" );
427 }
428 }
429 catch ( beans::UnknownPropertyException const & )
430 {
431 css::uno::Any anyEx = cppu::getCaughtException();
432 OSL_FAIL( "Property OpenMode not supported!" );
433
434 throw embed::StorageWrappedTargetException(
435 "Bug! Value of property OpenMode has wrong type!",
436 uno::Reference< uno::XInterface >(),
437 anyEx );
438 }
439 catch ( lang::WrappedTargetException const & )
440 {
441 css::uno::Any anyEx = cppu::getCaughtException();
442 OSL_FAIL( "Caught WrappedTargetException!" );
443
444 throw embed::StorageWrappedTargetException(
445 "WrappedTargetException during getPropertyValue!",
446 uno::Reference< uno::XInterface >(),
447 anyEx );
448 }
449 }
450 else
451 {
452 // sub storage
453
454 const OUString & rName = aUri.getDecodedName();
455
456 if ( eMode == READ )
457 {
458 try
459 {
460 sal_Int32 const nOpenMode = embed::ElementModes::READ
461 | embed::ElementModes::NOCREATE;
462 xStorage
463 = xParentStorage->openStorageElement( rName, nOpenMode );
464 }
465 catch ( io::IOException const & )
466 {
467 // Another chance: Try to clone storage.
468 xStorage = createTemporaryStorage();
469 xParentStorage->copyStorageElementLastCommitTo( rName,
470 xStorage );
471 }
472 }
473 else
474 {
475 sal_Int32 nOpenMode = embed::ElementModes::READWRITE;
476 if ( eMode == READ_WRITE_NOCREATE )
477 nOpenMode |= embed::ElementModes::NOCREATE;
478
479 xStorage = xParentStorage->openStorageElement( rName, nOpenMode );
480 }
481 }
482
483 OSL_ENSURE( xStorage.is() || ( eMode != READ_WRITE_CREATE ),
484 "StorageElementFactory::queryStorage - No storage!" );
485 return xStorage;
486}
487
488
489uno::Reference< io::XStream >
491 const uno::Reference< embed::XStorage > & xParentStorage,
492 const OUString & rUri,
493 const OUString & rPassword,
494 StorageAccessMode eMode,
495 bool bTruncate )
496{
497 osl::MutexGuard aGuard( m_aMutex );
498
499 if ( !xParentStorage.is() )
500 {
501 throw lang::IllegalArgumentException(
502 "No parent storage!",
503 uno::Reference< uno::XInterface >(),
504 sal_Int16( 2 ) );
505 }
506
507 Uri aUri( rUri );
508 if ( aUri.isRoot() )
509 {
510 throw lang::IllegalArgumentException(
511 "Root never is a stream!",
512 uno::Reference< uno::XInterface >(),
513 sal_Int16( 2 ) );
514 }
515 else if ( aUri.isDocument() )
516 {
517 throw lang::IllegalArgumentException(
518 "A document never is a stream!",
519 uno::Reference< uno::XInterface >(),
520 sal_Int16( 2 ) );
521 }
522
523 sal_Int32 nOpenMode;
524 switch ( eMode )
525 {
526 case READ:
527 nOpenMode = embed::ElementModes::READ
528 | embed::ElementModes::NOCREATE
529 | embed::ElementModes::SEEKABLE;
530 break;
531
533 nOpenMode = embed::ElementModes::READWRITE
534 | embed::ElementModes::NOCREATE
535 | embed::ElementModes::SEEKABLE;
536
537 if ( bTruncate )
538 nOpenMode |= embed::ElementModes::TRUNCATE;
539
540 break;
541
543 nOpenMode = embed::ElementModes::READWRITE
544 | embed::ElementModes::SEEKABLE;
545
546 if ( bTruncate )
547 nOpenMode |= embed::ElementModes::TRUNCATE;
548
549 break;
550
551 default:
552 OSL_FAIL( "StorageElementFactory::queryStream : Unknown open mode!" );
553
554 throw embed::InvalidStorageException(
555 "Unknown open mode!",
556 uno::Reference< uno::XInterface >() );
557 }
558
559 // No object re-usage mechanism; streams are seekable => not stateless.
560
561 uno::Reference< io::XStream > xStream;
562 if ( !rPassword.isEmpty() )
563 {
564 if ( eMode == READ )
565 {
566 try
567 {
568 xStream = xParentStorage->cloneEncryptedStreamElement(
569 aUri.getDecodedName(),
570 rPassword );
571 }
572 catch ( packages::NoEncryptionException const & )
573 {
574 xStream
575 = xParentStorage->cloneStreamElement( aUri.getDecodedName() );
576 }
577 }
578 else
579 {
580 try
581 {
582 xStream = xParentStorage->openEncryptedStreamElement(
583 aUri.getDecodedName(),
584 nOpenMode,
585 rPassword );
586 }
587 catch ( packages::NoEncryptionException const & )
588 {
589 xStream
590 = xParentStorage->openStreamElement( aUri.getDecodedName(),
591 nOpenMode );
592 }
593 }
594 }
595 else
596 {
597 if ( eMode == READ )
598 {
599 xStream = xParentStorage->cloneStreamElement( aUri.getDecodedName() );
600 }
601 else
602 {
603 xStream = xParentStorage->openStreamElement( aUri.getDecodedName(),
604 nOpenMode );
605 }
606 }
607
608 if ( !xStream.is() )
609 {
610 throw embed::InvalidStorageException(
611 "No stream!",
612 uno::Reference< uno::XInterface >() );
613 }
614
615 return xStream;
616}
617
618/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XComponentContext > m_xContext
Reference< XInputStream > xStream
css::uno::Reference< css::embed::XStorage > createStorage(const OUString &rUri, StorageAccessMode eMode)
rtl::Reference< OfficeDocumentsManager > m_xDocsMgr
css::uno::Reference< css::embed::XStorage > queryParentStorage(const OUString &rUri, StorageAccessMode eMode)
css::uno::Reference< css::io::XInputStream > createInputStream(const OUString &rUri, const OUString &rPassword)
css::uno::Reference< css::io::XStream > queryStream(const css::uno::Reference< css::embed::XStorage > &xParentStorage, const OUString &rUri, const OUString &rPassword, StorageAccessMode eMode, bool bTruncate)
css::uno::Reference< css::io::XOutputStream > createOutputStream(const OUString &rUri, const OUString &rPassword, bool bTruncate)
css::uno::Reference< css::io::XStream > createStream(const OUString &rUri, const OUString &rPassword, bool bTruncate)
virtual ~StorageElementFactory() override
void releaseElement(Storage const *pElement)
css::uno::Reference< css::uno::XComponentContext > m_xContext
css::uno::Reference< css::embed::XStorage > queryStorage(const css::uno::Reference< css::embed::XStorage > &xParentStorage, const OUString &rUri, StorageAccessMode eMode)
css::uno::Reference< css::embed::XStorage > createTemporaryStorage()
StorageElementFactory::StorageMap::iterator m_aContainerIt
bool isRoot() const
Definition: tdoc_uri.hxx:93
const OUString & getDecodedName() const
Definition: tdoc_uri.hxx:74
const OUString & getDocumentId() const
Definition: tdoc_uri.hxx:68
const OUString & getParentUri() const
Definition: tdoc_uri.hxx:65
bool isDocument() const
Definition: tdoc_uri.hxx:99
Mode eMode
Any SAL_CALL getCaughtException()
@ READ_WRITE_NOCREATE
@ READ_WRITE_CREATE