LibreOffice Module unotools (master) 1
mediadescriptor.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
21#include <sal/log.hxx>
28
29#include <com/sun/star/ucb/ContentCreationException.hpp>
30#include <com/sun/star/ucb/XContent.hpp>
31#include <com/sun/star/task/XInteractionHandler.hpp>
32#include <com/sun/star/io/XStream.hpp>
33#include <com/sun/star/io/XActiveDataSink.hpp>
34#include <com/sun/star/io/XSeekable.hpp>
35#include <com/sun/star/lang/IllegalArgumentException.hpp>
36#include <com/sun/star/uri/UriReferenceFactory.hpp>
37#include <com/sun/star/uri/XUriReference.hpp>
38#include <com/sun/star/ucb/PostCommandArgument2.hpp>
39#include <officecfg/Office/Common.hxx>
40#include <ucbhelper/content.hxx>
44#include <tools/urlobj.hxx>
45#include <osl/diagnose.h>
47
48namespace utl {
49
50namespace {
51
52OUString removeFragment(OUString const & uri) {
53 css::uno::Reference< css::uri::XUriReference > ref(
54 css::uri::UriReferenceFactory::create(
56 parse(uri));
57 if (ref.is()) {
58 ref->clearFragment();
59 return ref->getUriReference();
60 } else {
61 SAL_WARN("unotools.misc", "cannot parse <" << uri << ">");
62 return uri;
63 }
64}
65
66}
67
69{
70}
71
72MediaDescriptor::MediaDescriptor(const css::uno::Sequence< css::beans::PropertyValue >& lSource)
73 : SequenceAsHashMap(lSource)
74{
75}
76
78{
79 bool bReadOnly = false;
80
81 // check for explicit readonly state
83 if (pIt != end())
84 {
85 pIt->second >>= bReadOnly;
86 return bReadOnly;
87 }
88
89 // streams based on post data are readonly by definition
91 if (pIt != end())
92 return true;
93
94 // A XStream capsulate XInputStream and XOutputStream ...
95 // If it exists - the file must be open in read/write mode!
97 if (pIt != end())
98 return false;
99
100 // Only file system content provider is able to provide XStream
101 // so for this content impossibility to create XStream triggers
102 // switch to readonly mode.
103 try
104 {
105 css::uno::Reference< css::ucb::XContent > xContent = getUnpackedValueOrDefault(MediaDescriptor::PROP_UCBCONTENT, css::uno::Reference< css::ucb::XContent >());
106 if (xContent.is())
107 {
108 css::uno::Reference< css::ucb::XContentIdentifier > xId = xContent->getIdentifier();
109 OUString aScheme;
110 if (xId.is())
111 aScheme = xId->getContentProviderScheme();
112
113 if (aScheme.equalsIgnoreAsciiCase("file"))
114 bReadOnly = true;
115 else
116 {
117 ::ucbhelper::Content aContent(xContent,
120 aContent.getPropertyValue("IsReadOnly") >>= bReadOnly;
121 }
122 }
123 }
124 catch(const css::uno::RuntimeException& )
125 { throw; }
126 catch(const css::uno::Exception&)
127 {}
128
129 return bReadOnly;
130}
131
132css::uno::Any MediaDescriptor::getComponentDataEntry( const OUString& rName ) const
133{
135 if( aPropertyIter != end() )
136 return comphelper::NamedValueCollection( aPropertyIter->second ).get( rName );
137 return css::uno::Any();
138}
139
140void MediaDescriptor::setComponentDataEntry( const OUString& rName, const css::uno::Any& rValue )
141{
142 if( rValue.hasValue() )
143 {
144 // get or create the 'ComponentData' property entry
145 css::uno::Any& rCompDataAny = operator[]( PROP_COMPONENTDATA );
146 // insert the value (retain sequence type, create NamedValue elements by default)
147 bool bHasNamedValues = !rCompDataAny.hasValue() || rCompDataAny.has< css::uno::Sequence< css::beans::NamedValue > >();
148 bool bHasPropValues = rCompDataAny.has< css::uno::Sequence< css::beans::PropertyValue > >();
149 OSL_ENSURE( bHasNamedValues || bHasPropValues, "MediaDescriptor::setComponentDataEntry - incompatible 'ComponentData' property in media descriptor" );
150 if( bHasNamedValues || bHasPropValues )
151 {
152 // insert or overwrite the passed value
153 comphelper::SequenceAsHashMap aCompDataMap( rCompDataAny );
154 aCompDataMap[ rName ] = rValue;
155 // write back the sequence (restore sequence with correct element type)
156 rCompDataAny = aCompDataMap.getAsConstAny( bHasPropValues );
157 }
158 }
159 else
160 {
161 // if an empty Any is passed, clear the entry
163 }
164}
165
166void MediaDescriptor::clearComponentDataEntry( const OUString& rName )
167{
169 if( aPropertyIter == end() )
170 return;
171
172 css::uno::Any& rCompDataAny = aPropertyIter->second;
173 bool bHasNamedValues = rCompDataAny.has< css::uno::Sequence< css::beans::NamedValue > >();
174 bool bHasPropValues = rCompDataAny.has< css::uno::Sequence< css::beans::PropertyValue > >();
175 OSL_ENSURE( bHasNamedValues || bHasPropValues, "MediaDescriptor::clearComponentDataEntry - incompatible 'ComponentData' property in media descriptor" );
176 if( bHasNamedValues || bHasPropValues )
177 {
178 // remove the value with the passed name
179 comphelper::SequenceAsHashMap aCompDataMap( rCompDataAny );
180 aCompDataMap.erase( rName );
181 // write back the sequence, or remove it completely if it is empty
182 if( aCompDataMap.empty() )
183 erase( aPropertyIter );
184 else
185 rCompDataAny = aCompDataMap.getAsConstAny( bHasPropValues );
186 }
187}
188
189css::uno::Sequence< css::beans::NamedValue > MediaDescriptor::requestAndVerifyDocPassword(
192 const ::std::vector< OUString >* pDefaultPasswords )
193{
194 css::uno::Sequence< css::beans::NamedValue > aMediaEncData = getUnpackedValueOrDefault(
195 PROP_ENCRYPTIONDATA, css::uno::Sequence< css::beans::NamedValue >() );
196 OUString aMediaPassword = getUnpackedValueOrDefault(
197 PROP_PASSWORD, OUString() );
198 css::uno::Reference< css::task::XInteractionHandler > xInteractHandler = getUnpackedValueOrDefault(
199 PROP_INTERACTIONHANDLER, css::uno::Reference< css::task::XInteractionHandler >() );
200 OUString aDocumentName = getUnpackedValueOrDefault(
201 PROP_URL, OUString() );
202
203 bool bIsDefaultPassword = false;
204 css::uno::Sequence< css::beans::NamedValue > aEncryptionData = comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
205 rVerifier, aMediaEncData, aMediaPassword, xInteractHandler, aDocumentName, eRequestType, pDefaultPasswords, &bIsDefaultPassword );
206
209
210 // insert encryption info into media descriptor
211 // TODO
212 if( aEncryptionData.hasElements() )
213 (*this)[ PROP_ENCRYPTIONDATA ] <<= aEncryptionData;
214
215 return aEncryptionData;
216}
217
219{
220 return impl_addInputStream( true );
221}
222
223/*-----------------------------------------------*/
225{
226 const bool bLock = !utl::ConfigManager::IsFuzzing()
228 return impl_addInputStream(bLock);
229}
230
231/*-----------------------------------------------*/
233{
234 // check for an already existing stream item first
236 if (pIt != end())
237 return true;
238
239 try
240 {
241 // No stream available - create a new one
242 // a) data comes as PostData ...
244 if (pIt != end())
245 {
246 const css::uno::Any& rPostData = pIt->second;
247 css::uno::Reference< css::io::XInputStream > xPostData;
248 rPostData >>= xPostData;
249
250 return impl_openStreamWithPostData( xPostData );
251 }
252
253 // b) ... or we must get it from the given URL
254 OUString sURL = getUnpackedValueOrDefault(MediaDescriptor::PROP_URL, OUString());
255 if (sURL.isEmpty())
256 throw css::uno::Exception("Found no URL.",
257 css::uno::Reference< css::uno::XInterface >());
258
259 return impl_openStreamWithURL( removeFragment(sURL), bLockFile );
260 }
261 catch(const css::uno::Exception&)
262 {
263 TOOLS_WARN_EXCEPTION("unotools.misc", "invalid MediaDescriptor detected");
264 return false;
265 }
266}
267
268bool MediaDescriptor::impl_openStreamWithPostData( const css::uno::Reference< css::io::XInputStream >& _rxPostData )
269{
270 if ( !_rxPostData.is() )
271 throw css::lang::IllegalArgumentException("Found invalid PostData.",
272 css::uno::Reference< css::uno::XInterface >(), 1);
273
274 // PostData can't be used in read/write mode!
275 (*this)[MediaDescriptor::PROP_READONLY] <<= true;
276
277 // prepare the environment
278 css::uno::Reference< css::task::XInteractionHandler > xInteraction = getUnpackedValueOrDefault(
280 css::uno::Reference< css::task::XInteractionHandler >());
281 css::uno::Reference< css::ucb::XProgressHandler > xProgress;
282 rtl::Reference<::ucbhelper::CommandEnvironment> xCommandEnv = new ::ucbhelper::CommandEnvironment(xInteraction, xProgress);
283
284 // media type
286 if (sMediaType.isEmpty())
287 {
288 sMediaType = "application/x-www-form-urlencoded";
290 }
291
292 // url
293 OUString sURL( getUnpackedValueOrDefault( PROP_URL, OUString() ) );
294
295 css::uno::Reference< css::io::XInputStream > xResultStream;
296 try
297 {
298 // seek PostData stream to the beginning
299 css::uno::Reference< css::io::XSeekable > xSeek( _rxPostData, css::uno::UNO_QUERY );
300 if ( xSeek.is() )
301 xSeek->seek( 0 );
302
303 // a content for the URL
304 ::ucbhelper::Content aContent( sURL, xCommandEnv, comphelper::getProcessComponentContext() );
305
306 // use post command
307 css::ucb::PostCommandArgument2 aPostArgument;
308 aPostArgument.Source = _rxPostData;
309 css::uno::Reference< css::io::XActiveDataSink > xSink( new ucbhelper::ActiveDataSink );
310 aPostArgument.Sink = xSink;
311 aPostArgument.MediaType = sMediaType;
312 aPostArgument.Referer = getUnpackedValueOrDefault( PROP_REFERRER, OUString() );
313
314 aContent.executeCommand( "post", css::uno::Any( aPostArgument ) );
315
316 // get result
317 xResultStream = xSink->getInputStream();
318 }
319 catch( const css::uno::Exception& )
320 {
321 }
322
323 // success?
324 if ( !xResultStream.is() )
325 {
326 OSL_FAIL( "no valid reply to the HTTP-Post" );
327 return false;
328 }
329
330 (*this)[MediaDescriptor::PROP_INPUTSTREAM] <<= xResultStream;
331 return true;
332}
333
334/*-----------------------------------------------*/
335bool MediaDescriptor::impl_openStreamWithURL( const OUString& sURL, bool bLockFile )
336{
337 OUString referer(getUnpackedValueOrDefault(PROP_REFERRER, OUString()));
339 return false;
340 }
341
342 // prepare the environment
343 css::uno::Reference< css::task::XInteractionHandler > xOrgInteraction = getUnpackedValueOrDefault(
345 css::uno::Reference< css::task::XInteractionHandler >());
346
347 css::uno::Reference< css::task::XInteractionHandler > xAuthenticationInteraction = getUnpackedValueOrDefault(
349 css::uno::Reference< css::task::XInteractionHandler >());
350
351 rtl::Reference<comphelper::StillReadWriteInteraction> xInteraction = new comphelper::StillReadWriteInteraction(xOrgInteraction,xAuthenticationInteraction);
352
353 css::uno::Reference< css::ucb::XProgressHandler > xProgress;
354 rtl::Reference<::ucbhelper::CommandEnvironment> xCommandEnv = new ::ucbhelper::CommandEnvironment(xInteraction, xProgress);
355
356 // try to create the content
357 // no content -> no stream => return immediately with FALSE
358 ::ucbhelper::Content aContent;
359 css::uno::Reference< css::ucb::XContent > xContent;
360 try
361 {
362 aContent = ::ucbhelper::Content(sURL, xCommandEnv, comphelper::getProcessComponentContext());
363 xContent = aContent.get();
364 }
365 catch(const css::uno::RuntimeException&)
366 { throw; }
367 catch(const css::ucb::ContentCreationException&)
368 {
369 TOOLS_WARN_EXCEPTION("unotools.misc", "url: '" << sURL << "'");
370 return false; // TODO error handling
371 }
372 catch(const css::uno::Exception&)
373 {
374 TOOLS_WARN_EXCEPTION("unotools.misc", "url: '" << sURL << "'");
375 return false; // TODO error handling
376 }
377
378 // try to open the file in read/write mode
379 // (if it's allowed to do so).
380 // But handle errors in a "hidden mode". Because
381 // we try it readonly later - if read/write is not an option.
382 css::uno::Reference< css::io::XStream > xStream;
383 css::uno::Reference< css::io::XInputStream > xInputStream;
384
385 bool bReadOnly = false;
386 bool bModeRequestedExplicitly = false;
388 if (pIt != end())
389 {
390 pIt->second >>= bReadOnly;
391 bModeRequestedExplicitly = true;
392 }
393
394 if ( !bReadOnly && bLockFile )
395 {
396 try
397 {
398 // TODO: use "special" still interaction to suppress error messages
399 xStream = aContent.openWriteableStream();
400 if (xStream.is())
401 xInputStream = xStream->getInputStream();
402 }
403 catch(const css::uno::RuntimeException&)
404 { throw; }
405 catch(const css::uno::Exception&)
406 {
407 css::uno::Any ex( cppu::getCaughtException() );
408 // ignore exception, if reason was problem reasoned on
409 // open it in WRITABLE mode! Then we try it READONLY
410 // later a second time.
411 // All other errors must be handled as real error an
412 // break this method.
413 if (!xInteraction->wasWriteError() || bModeRequestedExplicitly)
414 {
415 SAL_WARN("unotools.misc","url: '" << sURL << "' " << exceptionToString(ex));
416 // If the protocol is webdav, then we need to treat the stream as readonly, even if the
417 // operation was requested as read/write explicitly (the WebDAV UCB implementation is monodirectional
418 // read or write not both at the same time).
419 if ( !INetURLObject( sURL ).isAnyKnownWebDAVScheme() )
420 return false;
421 }
422 xStream.clear();
423 xInputStream.clear();
424 }
425 }
426
427 // If opening of the stream in read/write mode was not allowed
428 // or failed by an error - we must try it in readonly mode.
429 if (!xInputStream.is())
430 {
431 OUString aScheme;
432
433 try
434 {
435 css::uno::Reference< css::ucb::XContentIdentifier > xContId(
436 aContent.get().is() ? aContent.get()->getIdentifier() : nullptr );
437
438 if ( xContId.is() )
439 aScheme = xContId->getContentProviderScheme();
440
441 // Only file system content provider is able to provide XStream
442 // so for this content impossibility to create XStream triggers
443 // switch to readonly mode in case of opening with locking on
444 if( bLockFile && aScheme.equalsIgnoreAsciiCase("file") )
445 bReadOnly = true;
446 else
447 {
448 bool bRequestReadOnly = bReadOnly;
449 aContent.getPropertyValue("IsReadOnly") >>= bReadOnly;
450 if ( bReadOnly && !bRequestReadOnly && bModeRequestedExplicitly )
451 return false; // the document is explicitly requested with WRITABLE mode
452 }
453 }
454 catch(const css::uno::RuntimeException&)
455 { throw; }
456 catch(const css::uno::Exception&)
457 { /* no error handling if IsReadOnly property does not exist for UCP */ }
458
459 if ( bReadOnly )
461
462 xInteraction->resetInterceptions();
463 xInteraction->resetErrorStates();
464 try
465 {
466 // all the contents except file-URLs should be opened as usual
467 if ( bLockFile || !aScheme.equalsIgnoreAsciiCase("file") )
468 xInputStream = aContent.openStream();
469 else
470 xInputStream = aContent.openStreamNoLock();
471 }
472 catch(const css::uno::RuntimeException&)
473 {
474 throw;
475 }
476 catch(const css::uno::Exception&)
477 {
478 TOOLS_INFO_EXCEPTION("unotools.misc","url: '" << sURL << "'");
479 return false;
480 }
481 }
482
483 // add streams to the descriptor
484 if (xContent.is())
485 (*this)[MediaDescriptor::PROP_UCBCONTENT] <<= xContent;
486 if (xStream.is())
488 if (xInputStream.is())
489 (*this)[MediaDescriptor::PROP_INPUTSTREAM] <<= xInputStream;
490
491 // At least we need an input stream. The r/w stream is optional ...
492 return xInputStream.is();
493}
494
495} // namespace comphelper
496
497/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XInputStream > xStream
constexpr OUStringLiteral sMediaType
static css::uno::Sequence< css::beans::NamedValue > requestAndVerifyDocPassword(IDocPasswordVerifier &rVerifier, const css::uno::Sequence< css::beans::NamedValue > &rMediaEncData, const OUString &rMediaPassword, const css::uno::Reference< css::task::XInteractionHandler > &rxInteractHandler, const OUString &rDocumentUrl, DocPasswordRequestType eRequestType, const ::std::vector< OUString > *pDefaultPasswords=nullptr, bool *pbIsDefaultPassword=nullptr)
const css::uno::Any & get(const OUString &_rValueName) const
css::uno::Any getAsConstAny(bool bAsPropertyValue) const
iterator erase(iterator it)
TValueType getUnpackedValueOrDefault(const OUString &sKey, const TValueType &aDefault) const
iterator find(const OUString &rKey)
SequenceAsHashMapBase::iterator iterator
css::uno::Any & operator[](const OUString &rKey)
SequenceAsHashMapBase::const_iterator const_iterator
css::uno::Any getPropertyValue(const OUString &rPropertyName)
css::uno::Reference< css::io::XStream > openWriteableStream()
css::uno::Reference< css::io::XInputStream > openStreamNoLock()
css::uno::Any executeCommand(const OUString &rCommandName, const css::uno::Any &rCommandArgument)
css::uno::Reference< css::ucb::XContent > get() const
css::uno::Reference< css::io::XInputStream > openStream()
static bool IsFuzzing()
Definition: configmgr.cxx:181
SAL_DLLPRIVATE bool impl_addInputStream(bool bLockFile)
it checks if the descriptor already has a valid InputStream item and creates a new one,...
static constexpr OUStringLiteral PROP_INPUTSTREAM
void setComponentDataEntry(const OUString &rName, const css::uno::Any &rValue)
Inserts a value into the sequence contained in the property 'ComponentData' of the media descriptor.
static constexpr OUStringLiteral PROP_STREAM
SAL_DLLPRIVATE bool impl_openStreamWithPostData(const css::uno::Reference< css::io::XInputStream > &_rxPostData)
tries to open a stream by using the given PostData stream.
bool isStreamReadOnly() const
it checks if the descriptor describes a readonly stream.
static constexpr OUStringLiteral PROP_MEDIATYPE
static constexpr OUStringLiteral PROP_PASSWORD
SAL_DLLPRIVATE bool impl_openStreamWithURL(const OUString &sURL, bool bLockFile)
tries to open a stream by using the given URL.
css::uno::Sequence< css::beans::NamedValue > requestAndVerifyDocPassword(comphelper::IDocPasswordVerifier &rVerifier, comphelper::DocPasswordRequestType eRequestType, const ::std::vector< OUString > *pDefaultPasswords)
This helper function tries to find a password for the document described by this media descriptor.
MediaDescriptor()
these ctors do nothing - excepting that they forward the given parameters to the base class ctors.
static constexpr OUStringLiteral PROP_URL
static constexpr OUStringLiteral PROP_AUTHENTICATIONHANDLER
bool addInputStream()
it checks if the descriptor already has a valid InputStream item and creates a new one,...
static constexpr OUStringLiteral PROP_ENCRYPTIONDATA
static constexpr OUStringLiteral PROP_REFERRER
static constexpr OUStringLiteral PROP_READONLY
bool addInputStreamOwnLock()
it checks if the descriptor already has a valid InputStream item and creates a new one,...
static constexpr OUStringLiteral PROP_UCBCONTENT
static constexpr OUStringLiteral PROP_COMPONENTDATA
static constexpr OUStringLiteral PROP_POSTDATA
void clearComponentDataEntry(const OUString &rName)
Removes a value from the sequence contained in the property 'ComponentData' of the media descriptor.
static constexpr OUStringLiteral PROP_INTERACTIONHANDLER
css::uno::Any getComponentDataEntry(const OUString &rName) const
Returns a value from the sequence contained in the property 'ComponentData' of this media descriptor.
OString exceptionToString(const css::uno::Any &caught)
#define TOOLS_WARN_EXCEPTION(area, stream)
#define TOOLS_INFO_EXCEPTION(area, stream)
bool bReadOnly
tools::SvRef< SvBaseLink > xSink
#define SAL_WARN(area, stream)
bool isUntrustedReferer(OUString const &referer)
Check whether the given referer URI is untrusted, and links originating from it should not be accesse...
OUString get(TranslateId sContextAndId, const std::locale &loc)
Definition: resmgr.cxx:211
Reference< XComponentContext > getProcessComponentContext()
Any SAL_CALL getCaughtException()
bool parse(OUString const &uri, SourceProviderScannerData *data)
UNOTOOLS_DLLPUBLIC css::uno::Reference< css::ucb::XCommandEnvironment > getDefaultCommandEnvironment()
Returns a default XCommandEnvironment to be used when creating a ucbhelper::Content.
Definition: ucbhelper.cxx:117