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