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