Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / unotools / source / misc / mediadescriptor.cxx
blobb8bb7f13469e92e56686a1dc2f8229451308faf4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <comphelper/docpasswordhelper.hxx>
21 #include <sal/log.hxx>
22 #include <unotools/configmgr.hxx>
23 #include <unotools/mediadescriptor.hxx>
24 #include <unotools/securityoptions.hxx>
25 #include <unotools/ucbhelper.hxx>
26 #include <comphelper/namedvaluecollection.hxx>
27 #include <comphelper/stillreadwriteinteraction.hxx>
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>
41 #include <ucbhelper/commandenvironment.hxx>
42 #include <ucbhelper/activedatasink.hxx>
43 #include <comphelper/processfactory.hxx>
44 #include <tools/urlobj.hxx>
45 #include <osl/diagnose.h>
46 #include <comphelper/diagnose_ex.hxx>
48 namespace utl {
50 namespace {
52 OUString removeFragment(OUString const & uri) {
53 css::uno::Reference< css::uri::XUriReference > ref(
54 css::uri::UriReferenceFactory::create(
55 comphelper::getProcessComponentContext())->
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;
68 MediaDescriptor::MediaDescriptor()
72 MediaDescriptor::MediaDescriptor(const css::uno::Sequence< css::beans::PropertyValue >& lSource)
73 : SequenceAsHashMap(lSource)
77 bool MediaDescriptor::isStreamReadOnly() const
79 bool bReadOnly = false;
81 // check for explicit readonly state
82 const_iterator pIt = find(MediaDescriptor::PROP_READONLY);
83 if (pIt != end())
85 pIt->second >>= bReadOnly;
86 return bReadOnly;
89 // streams based on post data are readonly by definition
90 pIt = find(MediaDescriptor::PROP_POSTDATA);
91 if (pIt != end())
92 return true;
94 // A XStream capsulate XInputStream and XOutputStream ...
95 // If it exists - the file must be open in read/write mode!
96 pIt = find(MediaDescriptor::PROP_STREAM);
97 if (pIt != end())
98 return false;
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.
105 css::uno::Reference< css::ucb::XContent > xContent = getUnpackedValueOrDefault(MediaDescriptor::PROP_UCBCONTENT, css::uno::Reference< css::ucb::XContent >());
106 if (xContent.is())
108 css::uno::Reference< css::ucb::XContentIdentifier > xId = xContent->getIdentifier();
109 OUString aScheme;
110 if (xId.is())
111 aScheme = xId->getContentProviderScheme();
113 if (aScheme.equalsIgnoreAsciiCase("file"))
114 bReadOnly = true;
115 else
117 ::ucbhelper::Content aContent(xContent,
118 utl::UCBContentHelper::getDefaultCommandEnvironment(),
119 comphelper::getProcessComponentContext());
120 aContent.getPropertyValue("IsReadOnly") >>= bReadOnly;
124 catch(const css::uno::RuntimeException& )
125 { throw; }
126 catch(const css::uno::Exception&)
129 return bReadOnly;
132 css::uno::Any MediaDescriptor::getComponentDataEntry( const OUString& rName ) const
134 comphelper::SequenceAsHashMap::const_iterator aPropertyIter = find( PROP_COMPONENTDATA );
135 if( aPropertyIter != end() )
136 return comphelper::NamedValueCollection( aPropertyIter->second ).get( rName );
137 return css::uno::Any();
140 void MediaDescriptor::setComponentDataEntry( const OUString& rName, const css::uno::Any& rValue )
142 if( rValue.hasValue() )
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 )
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 );
159 else
161 // if an empty Any is passed, clear the entry
162 clearComponentDataEntry( rName );
166 void MediaDescriptor::clearComponentDataEntry( const OUString& rName )
168 comphelper::SequenceAsHashMap::iterator aPropertyIter = find( PROP_COMPONENTDATA );
169 if( aPropertyIter == end() )
170 return;
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 )
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 );
189 css::uno::Sequence< css::beans::NamedValue > MediaDescriptor::requestAndVerifyDocPassword(
190 comphelper::IDocPasswordVerifier& rVerifier,
191 comphelper::DocPasswordRequestType eRequestType,
192 const ::std::vector< OUString >* pDefaultPasswords )
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() );
203 bool bIsDefaultPassword = false;
204 css::uno::Sequence< css::beans::NamedValue > aEncryptionData = comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
205 rVerifier, aMediaEncData, aMediaPassword, xInteractHandler, aDocumentName, eRequestType, pDefaultPasswords, &bIsDefaultPassword );
207 erase( PROP_PASSWORD );
208 erase( PROP_ENCRYPTIONDATA );
210 // insert encryption info into media descriptor
211 // TODO
212 if( aEncryptionData.hasElements() )
213 (*this)[ PROP_ENCRYPTIONDATA ] <<= aEncryptionData;
215 return aEncryptionData;
218 bool MediaDescriptor::addInputStream()
220 return impl_addInputStream( true );
223 /*-----------------------------------------------*/
224 bool MediaDescriptor::addInputStreamOwnLock()
226 const bool bLock = !utl::ConfigManager::IsFuzzing()
227 && officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get();
228 return impl_addInputStream(bLock);
231 /*-----------------------------------------------*/
232 bool MediaDescriptor::impl_addInputStream( bool bLockFile )
234 // check for an already existing stream item first
235 const_iterator pIt = find(MediaDescriptor::PROP_INPUTSTREAM);
236 if (pIt != end())
237 return true;
241 // No stream available - create a new one
242 // a) data comes as PostData ...
243 pIt = find(MediaDescriptor::PROP_POSTDATA);
244 if (pIt != end())
246 const css::uno::Any& rPostData = pIt->second;
247 css::uno::Reference< css::io::XInputStream > xPostData;
248 rPostData >>= xPostData;
250 return impl_openStreamWithPostData( xPostData );
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 >());
259 return impl_openStreamWithURL( removeFragment(sURL), bLockFile );
261 catch(const css::uno::Exception&)
263 TOOLS_WARN_EXCEPTION("unotools.misc", "invalid MediaDescriptor detected");
264 return false;
268 bool MediaDescriptor::impl_openStreamWithPostData( const css::uno::Reference< css::io::XInputStream >& _rxPostData )
270 if ( !_rxPostData.is() )
271 throw css::lang::IllegalArgumentException("Found invalid PostData.",
272 css::uno::Reference< css::uno::XInterface >(), 1);
274 // PostData can't be used in read/write mode!
275 (*this)[MediaDescriptor::PROP_READONLY] <<= true;
277 // prepare the environment
278 css::uno::Reference< css::task::XInteractionHandler > xInteraction = getUnpackedValueOrDefault(
279 MediaDescriptor::PROP_INTERACTIONHANDLER,
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);
284 // media type
285 OUString sMediaType = getUnpackedValueOrDefault(MediaDescriptor::PROP_MEDIATYPE, OUString());
286 if (sMediaType.isEmpty())
288 sMediaType = "application/x-www-form-urlencoded";
289 (*this)[MediaDescriptor::PROP_MEDIATYPE] <<= sMediaType;
292 // url
293 OUString sURL( getUnpackedValueOrDefault( PROP_URL, OUString() ) );
295 css::uno::Reference< css::io::XInputStream > xResultStream;
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 );
303 // a content for the URL
304 ::ucbhelper::Content aContent( sURL, xCommandEnv, comphelper::getProcessComponentContext() );
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() );
314 aContent.executeCommand( "post", css::uno::Any( aPostArgument ) );
316 // get result
317 xResultStream = xSink->getInputStream();
319 catch( const css::uno::Exception& )
323 // success?
324 if ( !xResultStream.is() )
326 OSL_FAIL( "no valid reply to the HTTP-Post" );
327 return false;
330 (*this)[MediaDescriptor::PROP_INPUTSTREAM] <<= xResultStream;
331 return true;
334 /*-----------------------------------------------*/
335 bool MediaDescriptor::impl_openStreamWithURL( const OUString& sURL, bool bLockFile )
337 OUString referer(getUnpackedValueOrDefault(PROP_REFERRER, OUString()));
338 if (SvtSecurityOptions::isUntrustedReferer(referer)) {
339 return false;
342 // prepare the environment
343 css::uno::Reference< css::task::XInteractionHandler > xOrgInteraction = getUnpackedValueOrDefault(
344 MediaDescriptor::PROP_INTERACTIONHANDLER,
345 css::uno::Reference< css::task::XInteractionHandler >());
347 css::uno::Reference< css::task::XInteractionHandler > xAuthenticationInteraction = getUnpackedValueOrDefault(
348 MediaDescriptor::PROP_AUTHENTICATIONHANDLER,
349 css::uno::Reference< css::task::XInteractionHandler >());
351 rtl::Reference<comphelper::StillReadWriteInteraction> xInteraction = new comphelper::StillReadWriteInteraction(xOrgInteraction,xAuthenticationInteraction);
353 css::uno::Reference< css::ucb::XProgressHandler > xProgress;
354 rtl::Reference<::ucbhelper::CommandEnvironment> xCommandEnv = new ::ucbhelper::CommandEnvironment(xInteraction, xProgress);
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;
362 aContent = ::ucbhelper::Content(sURL, xCommandEnv, comphelper::getProcessComponentContext());
363 xContent = aContent.get();
365 catch(const css::uno::RuntimeException&)
366 { throw; }
367 catch(const css::ucb::ContentCreationException&)
369 TOOLS_WARN_EXCEPTION("unotools.misc", "url: '" << sURL << "'");
370 return false; // TODO error handling
372 catch(const css::uno::Exception&)
374 TOOLS_WARN_EXCEPTION("unotools.misc", "url: '" << sURL << "'");
375 return false; // TODO error handling
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;
385 bool bReadOnly = false;
386 bool bModeRequestedExplicitly = false;
387 const_iterator pIt = find(MediaDescriptor::PROP_READONLY);
388 if (pIt != end())
390 pIt->second >>= bReadOnly;
391 bModeRequestedExplicitly = true;
394 if ( !bReadOnly && bLockFile )
398 // TODO: use "special" still interaction to suppress error messages
399 xStream = aContent.openWriteableStream();
400 if (xStream.is())
401 xInputStream = xStream->getInputStream();
403 catch(const css::uno::RuntimeException&)
404 { throw; }
405 catch(const css::uno::Exception&)
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)
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;
422 xStream.clear();
423 xInputStream.clear();
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())
431 OUString aScheme;
435 css::uno::Reference< css::ucb::XContentIdentifier > xContId(
436 aContent.get().is() ? aContent.get()->getIdentifier() : nullptr );
438 if ( xContId.is() )
439 aScheme = xContId->getContentProviderScheme();
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
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
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 */ }
459 if ( bReadOnly )
460 (*this)[MediaDescriptor::PROP_READONLY] <<= bReadOnly;
462 xInteraction->resetInterceptions();
463 xInteraction->resetErrorStates();
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();
472 catch(const css::uno::RuntimeException&)
474 throw;
476 catch(const css::uno::Exception&)
478 TOOLS_INFO_EXCEPTION("unotools.misc","url: '" << sURL << "'");
479 return false;
483 // add streams to the descriptor
484 if (xContent.is())
485 (*this)[MediaDescriptor::PROP_UCBCONTENT] <<= xContent;
486 if (xStream.is())
487 (*this)[MediaDescriptor::PROP_STREAM] <<= xStream;
488 if (xInputStream.is())
489 (*this)[MediaDescriptor::PROP_INPUTSTREAM] <<= xInputStream;
491 // At least we need an input stream. The r/w stream is optional ...
492 return xInputStream.is();
495 } // namespace comphelper
497 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */