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>
27 #include <cppuhelper/exc_hlp.hxx>
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 
36 using namespace com::sun::star;
37 using namespace tdoc_ucp;
38 
39 
40 // StorageElementFactory Implementation.
41 
42 
43 StorageElementFactory::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 
59 uno::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 
80 uno::Reference< embed::XStorage >
81 StorageElementFactory::createStorage( const OUString & rUri,
82  StorageAccessMode eMode )
83 {
84  osl::MutexGuard aGuard( m_aMutex );
85 
86  if ( ( eMode != READ ) &&
87  ( eMode != READ_WRITE_NOCREATE ) &&
88  ( eMode != READ_WRITE_CREATE ) )
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 
124  case READ_WRITE_NOCREATE:
125  case READ_WRITE_CREATE:
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 
170  rtl::Reference< Storage > xElement(
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  rtl::Reference< Storage > xElement( aIt->second );
183  osl_atomic_decrement( &aIt->second->m_refCount );
184  return aIt->second;
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  aIt->second = new Storage( m_xContext, this, aUriKey, xParentStorage, xStorage );
219  aIt->second->m_aContainerIt = aIt;
220  return aIt->second;
221  }
222 }
223 
224 
225 uno::Reference< io::XInputStream >
227  const OUString & rPassword )
228 {
229  osl::MutexGuard aGuard( m_aMutex );
230 
231  uno::Reference< embed::XStorage > xParentStorage
232  = queryParentStorage( rUri, READ );
233 
234  // Each stream must have a parent storage.
235  if ( !xParentStorage.is() )
236  return uno::Reference< io::XInputStream >();
237 
238  uno::Reference< io::XStream > xStream
239  = queryStream( xParentStorage, rUri, rPassword, READ, false );
240 
241  if ( !xStream.is() )
242  return uno::Reference< io::XInputStream >();
243 
244  return xStream->getInputStream();
245 }
246 
247 
248 uno::Reference< io::XOutputStream >
250  const OUString & rPassword,
251  bool bTruncate )
252 {
253  osl::MutexGuard aGuard( m_aMutex );
254 
255  uno::Reference< embed::XStorage > xParentStorage
257 
258  // Each stream must have a parent storage.
259  if ( !xParentStorage.is() )
260  {
261  OSL_FAIL( "StorageElementFactory::createOutputStream - "
262  "Unable to create parent storage!" );
263  return uno::Reference< io::XOutputStream >();
264  }
265 
266  uno::Reference< io::XStream > xStream
267  = queryStream(
268  xParentStorage, rUri, rPassword, READ_WRITE_CREATE, bTruncate );
269 
270  if ( !xStream.is() )
271  {
272  OSL_FAIL( "StorageElementFactory::createOutputStream - "
273  "Unable to create stream!" );
274  return uno::Reference< io::XOutputStream >();
275  }
276 
277  // Note: We need a wrapper to hold a reference to the parent storage to
278  // ensure that nobody else owns it at the moment we want to commit
279  // our changes. (There can be only one writable instance at a time
280  // and even no writable instance if there is already another
281  // read-only instance!)
282  return uno::Reference< io::XOutputStream >(
283  new OutputStream( m_xContext, rUri, xParentStorage, xStream->getOutputStream() ) );
284 }
285 
286 
287 uno::Reference< io::XStream >
288 StorageElementFactory::createStream( const OUString & rUri,
289  const OUString & rPassword,
290  bool bTruncate )
291 {
292  osl::MutexGuard aGuard( m_aMutex );
293 
294  uno::Reference< embed::XStorage > xParentStorage
296 
297  // Each stream must have a parent storage.
298  if ( !xParentStorage.is() )
299  {
300  OSL_FAIL( "StorageElementFactory::createStream - "
301  "Unable to create parent storage!" );
302  return uno::Reference< io::XStream >();
303  }
304 
305  uno::Reference< io::XStream > xStream
306  = queryStream(
307  xParentStorage, rUri, rPassword, READ_WRITE_NOCREATE, bTruncate );
308 
309  if ( !xStream.is() )
310  {
311  OSL_FAIL( "StorageElementFactory::createStream - "
312  "Unable to create stream!" );
313  return uno::Reference< io::XStream >();
314  }
315 
316  return uno::Reference< io::XStream >(
317  new Stream( m_xContext, rUri, xParentStorage, xStream ) );
318 }
319 
320 
322 {
323  OSL_ASSERT( pElement );
324  osl::MutexGuard aGuard( m_aMutex );
325  if ( pElement->m_aContainerIt != m_aMap.end() )
326  m_aMap.erase( pElement->m_aContainerIt );
327 }
328 
329 
330 // Non-UNO interface
331 
332 
333 uno::Reference< embed::XStorage > StorageElementFactory::queryParentStorage(
334  const OUString & rUri, StorageAccessMode eMode )
335 {
336  uno::Reference< embed::XStorage > xParentStorage;
337 
338  Uri aUri( rUri );
339  Uri aParentUri( aUri.getParentUri() );
340  if ( !aParentUri.isRoot() )
341  {
342  xParentStorage = createStorage( aUri.getParentUri(), eMode );
343  OSL_ENSURE( xParentStorage.is()
344  // requested to create new storage, but failed?
345  || ( eMode != READ_WRITE_CREATE ),
346  "StorageElementFactory::queryParentStorage - No storage!" );
347  }
348  return xParentStorage;
349 }
350 
351 
352 uno::Reference< embed::XStorage > StorageElementFactory::queryStorage(
353  const uno::Reference< embed::XStorage > & xParentStorage,
354  const OUString & rUri,
355  StorageAccessMode eMode )
356 {
357  uno::Reference< embed::XStorage > xStorage;
358 
359  Uri aUri( rUri );
360 
361  if ( !xParentStorage.is() )
362  {
363  // document storage
364 
365  xStorage = m_xDocsMgr->queryStorage( aUri.getDocumentId() );
366 
367  if ( !xStorage.is() )
368  {
369  if ( eMode == READ_WRITE_CREATE )
370  throw lang::IllegalArgumentException(
371  "Invalid open mode: document storages cannot be created!",
372  uno::Reference< uno::XInterface >(),
373  sal_Int16( 2 ) );
374  else
375  throw embed::InvalidStorageException(
376  "Invalid document id!",
377  uno::Reference< uno::XInterface >() );
378  }
379 
380  // match xStorage's open mode against requested open mode
381 
382  uno::Reference< beans::XPropertySet > xPropSet(
383  xStorage, uno::UNO_QUERY );
384  OSL_ENSURE( xPropSet.is(),
385  "StorageElementFactory::queryStorage - "
386  "No XPropertySet interface!" );
387  try
388  {
389  uno::Any aPropValue = xPropSet->getPropertyValue("OpenMode");
390 
391  sal_Int32 nOpenMode = 0;
392  if ( aPropValue >>= nOpenMode )
393  {
394  switch ( eMode )
395  {
396  case READ:
397  if ( !( nOpenMode & embed::ElementModes::READ ) )
398  {
399  // document opened, but not readable.
400  throw embed::InvalidStorageException(
401  "Storage is open, but not readable!" );
402  }
403  // storage okay
404  break;
405 
406  case READ_WRITE_NOCREATE:
407  case READ_WRITE_CREATE:
408  if ( !( nOpenMode & embed::ElementModes::WRITE ) )
409  {
410  // document opened, but not writable.
411  throw embed::InvalidStorageException(
412  "Storage is open, but not writable!" );
413  }
414  // storage okay
415  break;
416  }
417  }
418  else
419  {
420  OSL_FAIL(
421  "Bug! Value of property OpenMode has wrong type!" );
422 
423  throw uno::RuntimeException(
424  "Bug! Value of property OpenMode has wrong type!" );
425  }
426  }
427  catch ( beans::UnknownPropertyException const & )
428  {
429  css::uno::Any anyEx = cppu::getCaughtException();
430  OSL_FAIL( "Property OpenMode not supported!" );
431 
432  throw embed::StorageWrappedTargetException(
433  "Bug! Value of property OpenMode has wrong type!",
434  uno::Reference< uno::XInterface >(),
435  anyEx );
436  }
437  catch ( lang::WrappedTargetException const & )
438  {
439  css::uno::Any anyEx = cppu::getCaughtException();
440  OSL_FAIL( "Caught WrappedTargetException!" );
441 
442  throw embed::StorageWrappedTargetException(
443  "WrappedTargetException during getPropertyValue!",
444  uno::Reference< uno::XInterface >(),
445  anyEx );
446  }
447  }
448  else
449  {
450  // sub storage
451 
452  const OUString & rName = aUri.getDecodedName();
453 
454  if ( eMode == READ )
455  {
456  try
457  {
458  sal_Int32 const nOpenMode = embed::ElementModes::READ
459  | embed::ElementModes::NOCREATE;
460  xStorage
461  = xParentStorage->openStorageElement( rName, nOpenMode );
462  }
463  catch ( io::IOException const & )
464  {
465  // Another chance: Try to clone storage.
466  xStorage = createTemporaryStorage();
467  xParentStorage->copyStorageElementLastCommitTo( rName,
468  xStorage );
469  }
470  }
471  else
472  {
473  sal_Int32 nOpenMode = embed::ElementModes::READWRITE;
474  if ( eMode == READ_WRITE_NOCREATE )
475  nOpenMode |= embed::ElementModes::NOCREATE;
476 
477  xStorage = xParentStorage->openStorageElement( rName, nOpenMode );
478  }
479  }
480 
481  OSL_ENSURE( xStorage.is() || ( eMode != READ_WRITE_CREATE ),
482  "StorageElementFactory::queryStorage - No storage!" );
483  return xStorage;
484 }
485 
486 
487 uno::Reference< io::XStream >
489  const uno::Reference< embed::XStorage > & xParentStorage,
490  const OUString & rUri,
491  const OUString & rPassword,
492  StorageAccessMode eMode,
493  bool bTruncate )
494 {
495  osl::MutexGuard aGuard( m_aMutex );
496 
497  if ( !xParentStorage.is() )
498  {
499  throw lang::IllegalArgumentException(
500  "No parent storage!",
501  uno::Reference< uno::XInterface >(),
502  sal_Int16( 2 ) );
503  }
504 
505  Uri aUri( rUri );
506  if ( aUri.isRoot() )
507  {
508  throw lang::IllegalArgumentException(
509  "Root never is a stream!",
510  uno::Reference< uno::XInterface >(),
511  sal_Int16( 2 ) );
512  }
513  else if ( aUri.isDocument() )
514  {
515  throw lang::IllegalArgumentException(
516  "A document never is a stream!",
517  uno::Reference< uno::XInterface >(),
518  sal_Int16( 2 ) );
519  }
520 
521  sal_Int32 nOpenMode;
522  switch ( eMode )
523  {
524  case READ:
525  nOpenMode = embed::ElementModes::READ
526  | embed::ElementModes::NOCREATE
527  | embed::ElementModes::SEEKABLE;
528  break;
529 
530  case READ_WRITE_NOCREATE:
531  nOpenMode = embed::ElementModes::READWRITE
532  | embed::ElementModes::NOCREATE
533  | embed::ElementModes::SEEKABLE;
534 
535  if ( bTruncate )
536  nOpenMode |= embed::ElementModes::TRUNCATE;
537 
538  break;
539 
540  case READ_WRITE_CREATE:
541  nOpenMode = embed::ElementModes::READWRITE
542  | embed::ElementModes::SEEKABLE;
543 
544  if ( bTruncate )
545  nOpenMode |= embed::ElementModes::TRUNCATE;
546 
547  break;
548 
549  default:
550  OSL_FAIL( "StorageElementFactory::queryStream : Unknown open mode!" );
551 
552  throw embed::InvalidStorageException(
553  "Unknown open mode!",
554  uno::Reference< uno::XInterface >() );
555  }
556 
557  // No object re-usage mechanism; streams are seekable => not stateless.
558 
559  uno::Reference< io::XStream > xStream;
560  if ( !rPassword.isEmpty() )
561  {
562  if ( eMode == READ )
563  {
564  try
565  {
566  xStream = xParentStorage->cloneEncryptedStreamElement(
567  aUri.getDecodedName(),
568  rPassword );
569  }
570  catch ( packages::NoEncryptionException const & )
571  {
572  xStream
573  = xParentStorage->cloneStreamElement( aUri.getDecodedName() );
574  }
575  }
576  else
577  {
578  try
579  {
580  xStream = xParentStorage->openEncryptedStreamElement(
581  aUri.getDecodedName(),
582  nOpenMode,
583  rPassword );
584  }
585  catch ( packages::NoEncryptionException const & )
586  {
587  xStream
588  = xParentStorage->openStreamElement( aUri.getDecodedName(),
589  nOpenMode );
590  }
591  }
592  }
593  else
594  {
595  if ( eMode == READ )
596  {
597  xStream = xParentStorage->cloneStreamElement( aUri.getDecodedName() );
598  }
599  else
600  {
601  xStream = xParentStorage->openStreamElement( aUri.getDecodedName(),
602  nOpenMode );
603  }
604  }
605 
606  if ( !xStream.is() )
607  {
608  throw embed::InvalidStorageException(
609  "No stream!",
610  uno::Reference< uno::XInterface >() );
611  }
612 
613  return xStream;
614 }
615 
616 /* 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